summaryrefslogtreecommitdiffstats
path: root/ipc/mscom/AgileReference.h
blob: d39e4444943e8f8289741bbbbc376f8e8987db77 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
/* -*- 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/Attributes.h"
#include "mozilla/RefPtr.h"
#include "nsISupportsImpl.h"

#include <objidl.h>

namespace mozilla {
namespace mscom {
namespace detail {

class MOZ_HEAP_CLASS GlobalInterfaceTableCookie final {
 public:
  GlobalInterfaceTableCookie(IUnknown* aObject, REFIID aIid,
                             HRESULT& aOutHResult);

  bool IsValid() const { return !!mCookie; }
  HRESULT GetInterface(REFIID aIid, void** aOutInterface) const;

  NS_INLINE_DECL_THREADSAFE_REFCOUNTING(GlobalInterfaceTableCookie)

  GlobalInterfaceTableCookie(const GlobalInterfaceTableCookie&) = delete;
  GlobalInterfaceTableCookie(GlobalInterfaceTableCookie&&) = delete;

  GlobalInterfaceTableCookie& operator=(const GlobalInterfaceTableCookie&) =
      delete;
  GlobalInterfaceTableCookie& operator=(GlobalInterfaceTableCookie&&) = delete;

 private:
  ~GlobalInterfaceTableCookie();

 private:
  DWORD mCookie;

 private:
  static IGlobalInterfaceTable* ObtainGit();
};

}  // namespace detail

/**
 * 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 final {
 public:
  AgileReference();

  template <typename InterfaceT>
  explicit AgileReference(RefPtr<InterfaceT>& aObject)
      : AgileReference(__uuidof(InterfaceT), aObject) {}

  AgileReference(REFIID aIid, IUnknown* aObject);

  AgileReference(const AgileReference& aOther) = default;
  AgileReference(AgileReference&& aOther);

  ~AgileReference();

  explicit operator bool() const {
    return mAgileRef || (mGitCookie && mGitCookie->IsValid());
  }

  HRESULT GetHResult() const { return mHResult; }

  template <typename T>
  void Assign(const RefPtr<T>& aOther) {
    Assign(__uuidof(T), aOther);
  }

  template <typename T>
  AgileReference& operator=(const RefPtr<T>& aOther) {
    Assign(aOther);
    return *this;
  }

  HRESULT Resolve(REFIID aIid, void** aOutInterface) const;

  AgileReference& operator=(const AgileReference& aOther);
  AgileReference& operator=(AgileReference&& aOther);

  AgileReference& operator=(decltype(nullptr)) {
    Clear();
    return *this;
  }

  void Clear();

 private:
  void Assign(REFIID aIid, IUnknown* aObject);
  void AssignInternal(IUnknown* aObject);

 private:
  IID mIid;
  RefPtr<IAgileReference> mAgileRef;
  RefPtr<detail::GlobalInterfaceTableCookie> mGitCookie;
  HRESULT mHResult;
};

}  // namespace mscom
}  // namespace mozilla

template <typename T>
RefPtr<T>::RefPtr(const mozilla::mscom::AgileReference& aAgileRef)
    : mRawPtr(nullptr) {
  (*this) = aAgileRef;
}

template <typename T>
RefPtr<T>& RefPtr<T>::operator=(
    const mozilla::mscom::AgileReference& aAgileRef) {
  void* newRawPtr;
  if (FAILED(aAgileRef.Resolve(__uuidof(T), &newRawPtr))) {
    newRawPtr = nullptr;
  }
  assign_assuming_AddRef(static_cast<T*>(newRawPtr));
  return *this;
}

#endif  // mozilla_mscom_AgileReference_h