Bug 1760868 [wpt PR 33305] - Update admins for wpt-live and wpt-pr-bot on GCP, a...
[gecko.git] / ipc / mscom / Interceptor.cpp
blob259d672683b6e43d7c934e8d9f3d89b62744c0ed
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 #define INITGUID
9 #include "mozilla/mscom/Interceptor.h"
11 #include <utility>
13 #include "MainThreadUtils.h"
14 #include "mozilla/Assertions.h"
15 #include "mozilla/ThreadLocal.h"
16 #include "mozilla/Unused.h"
17 #include "mozilla/mscom/DispatchForwarder.h"
18 #include "mozilla/mscom/FastMarshaler.h"
19 #include "mozilla/mscom/InterceptorLog.h"
20 #include "mozilla/mscom/MainThreadInvoker.h"
21 #include "mozilla/mscom/Objref.h"
22 #include "mozilla/mscom/Registration.h"
23 #include "mozilla/mscom/Utils.h"
24 #include "nsDirectoryServiceDefs.h"
25 #include "nsDirectoryServiceUtils.h"
26 #include "nsPrintfCString.h"
27 #include "nsRefPtrHashtable.h"
28 #include "nsThreadUtils.h"
29 #include "nsXULAppAPI.h"
31 #define ENSURE_HR_SUCCEEDED(hr) \
32 MOZ_ASSERT(SUCCEEDED((HRESULT)hr)); \
33 if (FAILED((HRESULT)hr)) { \
34 return hr; \
37 namespace mozilla {
38 namespace mscom {
39 namespace detail {
41 class LiveSet final {
42 public:
43 LiveSet() : mMutex("mozilla::mscom::LiveSet::mMutex") {}
45 void Lock() { mMutex.Lock(); }
47 void Unlock() { mMutex.Unlock(); }
49 void Put(IUnknown* aKey, already_AddRefed<IWeakReference> aValue) {
50 mMutex.AssertCurrentThreadOwns();
51 mLiveSet.InsertOrUpdate(aKey, RefPtr<IWeakReference>{std::move(aValue)});
54 RefPtr<IWeakReference> Get(IUnknown* aKey) {
55 mMutex.AssertCurrentThreadOwns();
56 RefPtr<IWeakReference> result;
57 mLiveSet.Get(aKey, getter_AddRefs(result));
58 return result;
61 void Remove(IUnknown* aKey) {
62 mMutex.AssertCurrentThreadOwns();
63 mLiveSet.Remove(aKey);
66 private:
67 Mutex mMutex MOZ_UNANNOTATED;
68 nsRefPtrHashtable<nsPtrHashKey<IUnknown>, IWeakReference> mLiveSet;
71 /**
72 * We don't use the normal XPCOM BaseAutoLock because we need the ability
73 * to explicitly Unlock.
75 class MOZ_RAII LiveSetAutoLock final {
76 public:
77 explicit LiveSetAutoLock(LiveSet& aLiveSet) : mLiveSet(&aLiveSet) {
78 aLiveSet.Lock();
81 ~LiveSetAutoLock() {
82 if (mLiveSet) {
83 mLiveSet->Unlock();
87 void Unlock() {
88 MOZ_ASSERT(mLiveSet);
89 if (mLiveSet) {
90 mLiveSet->Unlock();
91 mLiveSet = nullptr;
95 LiveSetAutoLock(const LiveSetAutoLock& aOther) = delete;
96 LiveSetAutoLock(LiveSetAutoLock&& aOther) = delete;
97 LiveSetAutoLock& operator=(const LiveSetAutoLock& aOther) = delete;
98 LiveSetAutoLock& operator=(LiveSetAutoLock&& aOther) = delete;
100 private:
101 LiveSet* mLiveSet;
104 class MOZ_RAII ReentrySentinel final {
105 public:
106 explicit ReentrySentinel(Interceptor* aCurrent) : mCurInterceptor(aCurrent) {
107 static const bool kHasTls = tlsSentinelStackTop.init();
108 MOZ_RELEASE_ASSERT(kHasTls);
110 mPrevSentinel = tlsSentinelStackTop.get();
111 tlsSentinelStackTop.set(this);
114 ~ReentrySentinel() { tlsSentinelStackTop.set(mPrevSentinel); }
116 bool IsOutermost() const {
117 return !(mPrevSentinel && mPrevSentinel->IsMarshaling(mCurInterceptor));
120 ReentrySentinel(const ReentrySentinel&) = delete;
121 ReentrySentinel(ReentrySentinel&&) = delete;
122 ReentrySentinel& operator=(const ReentrySentinel&) = delete;
123 ReentrySentinel& operator=(ReentrySentinel&&) = delete;
125 private:
126 bool IsMarshaling(Interceptor* aTopInterceptor) const {
127 return aTopInterceptor == mCurInterceptor ||
128 (mPrevSentinel && mPrevSentinel->IsMarshaling(aTopInterceptor));
131 private:
132 Interceptor* mCurInterceptor;
133 ReentrySentinel* mPrevSentinel;
135 static MOZ_THREAD_LOCAL(ReentrySentinel*) tlsSentinelStackTop;
138 MOZ_THREAD_LOCAL(ReentrySentinel*) ReentrySentinel::tlsSentinelStackTop;
140 class MOZ_RAII LoggedQIResult final {
141 public:
142 explicit LoggedQIResult(REFIID aIid)
143 : mIid(aIid),
144 mHr(E_UNEXPECTED),
145 mTarget(nullptr),
146 mInterceptor(nullptr),
147 mBegin(TimeStamp::Now()) {}
149 ~LoggedQIResult() {
150 if (!mTarget) {
151 return;
154 TimeStamp end(TimeStamp::Now());
155 TimeDuration total(end - mBegin);
156 TimeDuration overhead(total - mNonOverheadDuration);
158 InterceptorLog::QI(mHr, mTarget, mIid, mInterceptor, &overhead,
159 &mNonOverheadDuration);
162 void Log(IUnknown* aTarget, IUnknown* aInterceptor) {
163 mTarget = aTarget;
164 mInterceptor = aInterceptor;
167 void operator=(HRESULT aHr) { mHr = aHr; }
169 operator HRESULT() { return mHr; }
171 operator TimeDuration*() { return &mNonOverheadDuration; }
173 LoggedQIResult(const LoggedQIResult&) = delete;
174 LoggedQIResult(LoggedQIResult&&) = delete;
175 LoggedQIResult& operator=(const LoggedQIResult&) = delete;
176 LoggedQIResult& operator=(LoggedQIResult&&) = delete;
178 private:
179 REFIID mIid;
180 HRESULT mHr;
181 IUnknown* mTarget;
182 IUnknown* mInterceptor;
183 TimeDuration mNonOverheadDuration;
184 TimeStamp mBegin;
187 } // namespace detail
189 static detail::LiveSet& GetLiveSet() {
190 static detail::LiveSet sLiveSet;
191 return sLiveSet;
194 MOZ_THREAD_LOCAL(bool) Interceptor::tlsCreatingStdMarshal;
196 /* static */
197 HRESULT Interceptor::Create(STAUniquePtr<IUnknown> aTarget,
198 IInterceptorSink* aSink, REFIID aInitialIid,
199 void** aOutInterface) {
200 MOZ_ASSERT(aOutInterface && aTarget && aSink);
201 if (!aOutInterface) {
202 return E_INVALIDARG;
205 detail::LiveSetAutoLock lock(GetLiveSet());
207 RefPtr<IWeakReference> existingWeak(GetLiveSet().Get(aTarget.get()));
208 if (existingWeak) {
209 RefPtr<IWeakReferenceSource> existingStrong;
210 if (SUCCEEDED(existingWeak->ToStrongRef(getter_AddRefs(existingStrong)))) {
211 // QI on existingStrong may touch other threads. Since we now hold a
212 // strong ref on the interceptor, we may now release the lock.
213 lock.Unlock();
214 return existingStrong->QueryInterface(aInitialIid, aOutInterface);
218 *aOutInterface = nullptr;
220 if (!aTarget || !aSink) {
221 return E_INVALIDARG;
224 RefPtr<Interceptor> intcpt(new Interceptor(aSink));
225 return intcpt->GetInitialInterceptorForIID(lock, aInitialIid,
226 std::move(aTarget), aOutInterface);
229 Interceptor::Interceptor(IInterceptorSink* aSink)
230 : WeakReferenceSupport(WeakReferenceSupport::Flags::eDestroyOnMainThread),
231 mEventSink(aSink),
232 mInterceptorMapMutex("mozilla::mscom::Interceptor::mInterceptorMapMutex"),
233 mStdMarshalMutex("mozilla::mscom::Interceptor::mStdMarshalMutex"),
234 mStdMarshal(nullptr) {
235 static const bool kHasTls = tlsCreatingStdMarshal.init();
236 MOZ_ASSERT(kHasTls);
237 Unused << kHasTls;
239 MOZ_ASSERT(aSink);
240 RefPtr<IWeakReference> weakRef;
241 if (SUCCEEDED(GetWeakReference(getter_AddRefs(weakRef)))) {
242 aSink->SetInterceptor(weakRef);
246 Interceptor::~Interceptor() {
247 { // Scope for lock
248 detail::LiveSetAutoLock lock(GetLiveSet());
249 GetLiveSet().Remove(mTarget.get());
252 // This needs to run on the main thread because it releases target interface
253 // reference counts which may not be thread-safe.
254 MOZ_ASSERT(NS_IsMainThread());
255 for (uint32_t index = 0, len = mInterceptorMap.Length(); index < len;
256 ++index) {
257 MapEntry& entry = mInterceptorMap[index];
258 entry.mInterceptor = nullptr;
259 entry.mTargetInterface->Release();
263 HRESULT
264 Interceptor::GetClassForHandler(DWORD aDestContext, void* aDestContextPtr,
265 CLSID* aHandlerClsid) {
266 if (aDestContextPtr || !aHandlerClsid ||
267 aDestContext == MSHCTX_DIFFERENTMACHINE) {
268 return E_INVALIDARG;
271 MOZ_ASSERT(mEventSink);
272 return mEventSink->GetHandler(WrapNotNull(aHandlerClsid));
275 REFIID
276 Interceptor::MarshalAs(REFIID aIid) const {
277 #if defined(MOZ_MSCOM_REMARSHAL_NO_HANDLER)
278 return IsCallerExternalProcess() ? aIid : mEventSink->MarshalAs(aIid);
279 #else
280 return mEventSink->MarshalAs(aIid);
281 #endif // defined(MOZ_MSCOM_REMARSHAL_NO_HANDLER)
284 HRESULT
285 Interceptor::GetUnmarshalClass(REFIID riid, void* pv, DWORD dwDestContext,
286 void* pvDestContext, DWORD mshlflags,
287 CLSID* pCid) {
288 return mStdMarshal->GetUnmarshalClass(MarshalAs(riid), pv, dwDestContext,
289 pvDestContext, mshlflags, pCid);
292 HRESULT
293 Interceptor::GetMarshalSizeMax(REFIID riid, void* pv, DWORD dwDestContext,
294 void* pvDestContext, DWORD mshlflags,
295 DWORD* pSize) {
296 detail::ReentrySentinel sentinel(this);
298 HRESULT hr = mStdMarshal->GetMarshalSizeMax(
299 MarshalAs(riid), pv, dwDestContext, pvDestContext, mshlflags, pSize);
300 if (FAILED(hr) || !sentinel.IsOutermost()) {
301 return hr;
304 #if defined(MOZ_MSCOM_REMARSHAL_NO_HANDLER)
305 if (XRE_IsContentProcess() && IsCallerExternalProcess()) {
306 // The caller isn't our chrome process, so we do not provide a handler
307 // payload. Even though we're only getting the size here, calculating the
308 // payload size might actually require building the payload.
309 return hr;
311 #endif // defined(MOZ_MSCOM_REMARSHAL_NO_HANDLER)
313 DWORD payloadSize = 0;
314 hr = mEventSink->GetHandlerPayloadSize(WrapNotNull(this),
315 WrapNotNull(&payloadSize));
316 if (hr == E_NOTIMPL) {
317 return S_OK;
320 if (SUCCEEDED(hr)) {
321 *pSize += payloadSize;
323 return hr;
326 HRESULT
327 Interceptor::MarshalInterface(IStream* pStm, REFIID riid, void* pv,
328 DWORD dwDestContext, void* pvDestContext,
329 DWORD mshlflags) {
330 detail::ReentrySentinel sentinel(this);
332 HRESULT hr;
334 #if defined(MOZ_MSCOM_REMARSHAL_NO_HANDLER)
335 // Save the current stream position
336 LARGE_INTEGER seekTo;
337 seekTo.QuadPart = 0;
339 ULARGE_INTEGER objrefPos;
341 hr = pStm->Seek(seekTo, STREAM_SEEK_CUR, &objrefPos);
342 if (FAILED(hr)) {
343 return hr;
346 #endif // defined(MOZ_MSCOM_REMARSHAL_NO_HANDLER)
348 hr = mStdMarshal->MarshalInterface(pStm, MarshalAs(riid), pv, dwDestContext,
349 pvDestContext, mshlflags);
350 if (FAILED(hr) || !sentinel.IsOutermost()) {
351 return hr;
354 #if defined(MOZ_MSCOM_REMARSHAL_NO_HANDLER)
355 if (XRE_IsContentProcess() && IsCallerExternalProcess()) {
356 // The caller isn't our chrome process, so do not provide a handler.
358 // First, save the current position that marks the current end of the
359 // OBJREF in the stream.
360 ULARGE_INTEGER endPos;
361 hr = pStm->Seek(seekTo, STREAM_SEEK_CUR, &endPos);
362 if (FAILED(hr)) {
363 return hr;
366 // Now strip out the handler.
367 if (!StripHandlerFromOBJREF(WrapNotNull(pStm), objrefPos.QuadPart,
368 endPos.QuadPart)) {
369 return E_FAIL;
372 return S_OK;
374 #endif // defined(MOZ_MSCOM_REMARSHAL_NO_HANDLER)
376 hr = mEventSink->WriteHandlerPayload(WrapNotNull(this), WrapNotNull(pStm));
377 if (hr == E_NOTIMPL) {
378 return S_OK;
381 return hr;
384 HRESULT
385 Interceptor::UnmarshalInterface(IStream* pStm, REFIID riid, void** ppv) {
386 return mStdMarshal->UnmarshalInterface(pStm, riid, ppv);
389 HRESULT
390 Interceptor::ReleaseMarshalData(IStream* pStm) {
391 return mStdMarshal->ReleaseMarshalData(pStm);
394 HRESULT
395 Interceptor::DisconnectObject(DWORD dwReserved) {
396 mEventSink->DisconnectHandlerRemotes();
397 return mStdMarshal->DisconnectObject(dwReserved);
400 Interceptor::MapEntry* Interceptor::Lookup(REFIID aIid) {
401 mInterceptorMapMutex.AssertCurrentThreadOwns();
403 for (uint32_t index = 0, len = mInterceptorMap.Length(); index < len;
404 ++index) {
405 if (mInterceptorMap[index].mIID == aIid) {
406 return &mInterceptorMap[index];
409 return nullptr;
412 HRESULT
413 Interceptor::GetTargetForIID(REFIID aIid,
414 InterceptorTargetPtr<IUnknown>& aTarget) {
415 MutexAutoLock lock(mInterceptorMapMutex);
416 MapEntry* entry = Lookup(aIid);
417 if (entry) {
418 aTarget.reset(entry->mTargetInterface);
419 return S_OK;
422 return E_NOINTERFACE;
425 // CoGetInterceptor requires type metadata to be able to generate its emulated
426 // vtable. If no registered metadata is available, CoGetInterceptor returns
427 // kFileNotFound.
428 static const HRESULT kFileNotFound = HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND);
430 HRESULT
431 Interceptor::CreateInterceptor(REFIID aIid, IUnknown* aOuter,
432 IUnknown** aOutput) {
433 // In order to aggregate, we *must* request IID_IUnknown as the initial
434 // interface for the interceptor, as that IUnknown is non-delegating.
435 // This is a fundamental rule for creating aggregated objects in COM.
436 HRESULT hr = ::CoGetInterceptor(aIid, aOuter, IID_IUnknown, (void**)aOutput);
437 if (hr != kFileNotFound) {
438 return hr;
441 // In the case that CoGetInterceptor returns kFileNotFound, we can try to
442 // explicitly load typelib data from our runtime registration facility and
443 // pass that into CoGetInterceptorFromTypeInfo.
445 RefPtr<ITypeInfo> typeInfo;
446 bool found = RegisteredProxy::Find(aIid, getter_AddRefs(typeInfo));
447 // If this assert fires then we have omitted registering the typelib for a
448 // required interface. To fix this, review our calls to mscom::RegisterProxy
449 // and mscom::RegisterTypelib, and add the additional typelib as necessary.
450 MOZ_ASSERT(found);
451 if (!found) {
452 return kFileNotFound;
455 hr = ::CoGetInterceptorFromTypeInfo(aIid, aOuter, typeInfo, IID_IUnknown,
456 (void**)aOutput);
457 // If this assert fires then the interceptor doesn't like something about
458 // the format of the typelib. One thing in particular that it doesn't like
459 // is complex types that contain unions.
460 MOZ_ASSERT(SUCCEEDED(hr));
461 return hr;
464 HRESULT
465 Interceptor::PublishTarget(detail::LiveSetAutoLock& aLiveSetLock,
466 RefPtr<IUnknown> aInterceptor, REFIID aTargetIid,
467 STAUniquePtr<IUnknown> aTarget) {
468 RefPtr<IWeakReference> weakRef;
469 HRESULT hr = GetWeakReference(getter_AddRefs(weakRef));
470 if (FAILED(hr)) {
471 return hr;
474 // mTarget is a weak reference to aTarget. This is safe because we transfer
475 // ownership of aTarget into mInterceptorMap which remains live for the
476 // lifetime of this Interceptor.
477 mTarget = ToInterceptorTargetPtr(aTarget);
478 GetLiveSet().Put(mTarget.get(), weakRef.forget());
480 // Now we transfer aTarget's ownership into mInterceptorMap.
481 mInterceptorMap.AppendElement(
482 MapEntry(aTargetIid, aInterceptor, aTarget.release()));
484 // Release the live set lock because subsequent operations may post work to
485 // the main thread, creating potential for deadlocks.
486 aLiveSetLock.Unlock();
487 return S_OK;
490 HRESULT
491 Interceptor::GetInitialInterceptorForIID(detail::LiveSetAutoLock& aLiveSetLock,
492 REFIID aTargetIid,
493 STAUniquePtr<IUnknown> aTarget,
494 void** aOutInterceptor) {
495 MOZ_ASSERT(aOutInterceptor);
496 MOZ_ASSERT(aTargetIid != IID_IMarshal);
497 MOZ_ASSERT(!IsProxy(aTarget.get()));
499 HRESULT hr = E_UNEXPECTED;
501 auto hasFailed = [&hr]() -> bool { return FAILED(hr); };
503 auto cleanup = [&aLiveSetLock]() -> void { aLiveSetLock.Unlock(); };
505 ExecuteWhen<decltype(hasFailed), decltype(cleanup)> onFail(hasFailed,
506 cleanup);
508 if (aTargetIid == IID_IUnknown) {
509 // We must lock mInterceptorMapMutex so that nothing can race with us once
510 // we have been published to the live set.
511 MutexAutoLock lock(mInterceptorMapMutex);
513 hr = PublishTarget(aLiveSetLock, nullptr, aTargetIid, std::move(aTarget));
514 ENSURE_HR_SUCCEEDED(hr);
516 hr = QueryInterface(aTargetIid, aOutInterceptor);
517 ENSURE_HR_SUCCEEDED(hr);
518 return hr;
521 // Raise the refcount for stabilization purposes during aggregation
522 WeakReferenceSupport::StabilizeRefCount stabilizer(*this);
524 RefPtr<IUnknown> unkInterceptor;
525 hr = CreateInterceptor(aTargetIid, static_cast<WeakReferenceSupport*>(this),
526 getter_AddRefs(unkInterceptor));
527 ENSURE_HR_SUCCEEDED(hr);
529 RefPtr<ICallInterceptor> interceptor;
530 hr = unkInterceptor->QueryInterface(IID_ICallInterceptor,
531 getter_AddRefs(interceptor));
532 ENSURE_HR_SUCCEEDED(hr);
534 hr = interceptor->RegisterSink(mEventSink);
535 ENSURE_HR_SUCCEEDED(hr);
537 // We must lock mInterceptorMapMutex so that nothing can race with us once we
538 // have been published to the live set.
539 MutexAutoLock lock(mInterceptorMapMutex);
541 hr = PublishTarget(aLiveSetLock, unkInterceptor, aTargetIid,
542 std::move(aTarget));
543 ENSURE_HR_SUCCEEDED(hr);
545 if (MarshalAs(aTargetIid) == aTargetIid) {
546 hr = unkInterceptor->QueryInterface(aTargetIid, aOutInterceptor);
547 ENSURE_HR_SUCCEEDED(hr);
548 return hr;
551 hr = GetInterceptorForIID(aTargetIid, aOutInterceptor, &lock);
552 ENSURE_HR_SUCCEEDED(hr);
553 return hr;
556 HRESULT
557 Interceptor::GetInterceptorForIID(REFIID aIid, void** aOutInterceptor) {
558 return GetInterceptorForIID(aIid, aOutInterceptor, nullptr);
562 * This method contains the core guts of the handling of QueryInterface calls
563 * that are delegated to us from the ICallInterceptor.
565 * @param aIid ID of the desired interface
566 * @param aOutInterceptor The resulting emulated vtable that corresponds to
567 * the interface specified by aIid.
568 * @param aAlreadyLocked Proof of an existing lock on |mInterceptorMapMutex|,
569 * if present.
571 HRESULT
572 Interceptor::GetInterceptorForIID(REFIID aIid, void** aOutInterceptor,
573 MutexAutoLock* aAlreadyLocked) {
574 detail::LoggedQIResult result(aIid);
576 if (!aOutInterceptor) {
577 return E_INVALIDARG;
580 if (aIid == IID_IUnknown) {
581 // Special case: When we see IUnknown, we just provide a reference to this
582 RefPtr<IInterceptor> intcpt(this);
583 intcpt.forget(aOutInterceptor);
584 return S_OK;
587 REFIID interceptorIid = MarshalAs(aIid);
589 RefPtr<IUnknown> unkInterceptor;
590 IUnknown* interfaceForQILog = nullptr;
592 // (1) Check to see if we already have an existing interceptor for
593 // interceptorIid.
594 auto doLookup = [&]() -> void {
595 MapEntry* entry = Lookup(interceptorIid);
596 if (entry) {
597 unkInterceptor = entry->mInterceptor;
598 interfaceForQILog = entry->mTargetInterface;
602 if (aAlreadyLocked) {
603 doLookup();
604 } else {
605 MutexAutoLock lock(mInterceptorMapMutex);
606 doLookup();
609 // (1a) A COM interceptor already exists for this interface, so all we need
610 // to do is run a QI on it.
611 if (unkInterceptor) {
612 // Technically we didn't actually execute a QI on the target interface, but
613 // for logging purposes we would like to record the fact that this interface
614 // was requested.
615 result.Log(mTarget.get(), interfaceForQILog);
616 result = unkInterceptor->QueryInterface(interceptorIid, aOutInterceptor);
617 ENSURE_HR_SUCCEEDED(result);
618 return result;
621 // (2) Obtain a new target interface.
623 // (2a) First, make sure that the target interface is available
624 // NB: We *MUST* query the correct interface! ICallEvents::Invoke casts its
625 // pvReceiver argument directly to the required interface! DO NOT assume
626 // that COM will use QI or upcast/downcast!
627 HRESULT hr;
629 STAUniquePtr<IUnknown> targetInterface;
630 IUnknown* rawTargetInterface = nullptr;
631 hr =
632 QueryInterfaceTarget(interceptorIid, (void**)&rawTargetInterface, result);
633 targetInterface.reset(rawTargetInterface);
634 result = hr;
635 result.Log(mTarget.get(), targetInterface.get());
636 MOZ_ASSERT(SUCCEEDED(hr) || hr == E_NOINTERFACE);
637 if (hr == E_NOINTERFACE) {
638 return hr;
640 ENSURE_HR_SUCCEEDED(hr);
642 // We *really* shouldn't be adding interceptors to proxies
643 MOZ_ASSERT(aIid != IID_IMarshal);
645 // (3) Create a new COM interceptor to that interface that delegates its
646 // IUnknown to |this|.
648 // Raise the refcount for stabilization purposes during aggregation
649 WeakReferenceSupport::StabilizeRefCount stabilizer(*this);
651 hr = CreateInterceptor(interceptorIid,
652 static_cast<WeakReferenceSupport*>(this),
653 getter_AddRefs(unkInterceptor));
654 ENSURE_HR_SUCCEEDED(hr);
656 // (4) Obtain the interceptor's ICallInterceptor interface and register our
657 // event sink.
658 RefPtr<ICallInterceptor> interceptor;
659 hr = unkInterceptor->QueryInterface(IID_ICallInterceptor,
660 (void**)getter_AddRefs(interceptor));
661 ENSURE_HR_SUCCEEDED(hr);
663 hr = interceptor->RegisterSink(mEventSink);
664 ENSURE_HR_SUCCEEDED(hr);
666 // (5) Now that we have this new COM interceptor, insert it into the map.
667 auto doInsertion = [&]() -> void {
668 // We might have raced with another thread, so first check that we don't
669 // already have an entry for this
670 MapEntry* entry = Lookup(interceptorIid);
671 if (entry && entry->mInterceptor) {
672 // Bug 1433046: Because of aggregation, the QI for |interceptor|
673 // AddRefed |this|, not |unkInterceptor|. Thus, releasing |unkInterceptor|
674 // will destroy the object. Before we do that, we must first release
675 // |interceptor|. Otherwise, |interceptor| would be invalidated when
676 // |unkInterceptor| is destroyed.
677 interceptor = nullptr;
678 unkInterceptor = entry->mInterceptor;
679 } else {
680 // MapEntry has a RefPtr to unkInterceptor, OTOH we must not touch the
681 // refcount for the target interface because we are just moving it into
682 // the map and its refcounting might not be thread-safe.
683 IUnknown* rawTargetInterface = targetInterface.release();
684 mInterceptorMap.AppendElement(
685 MapEntry(interceptorIid, unkInterceptor, rawTargetInterface));
689 if (aAlreadyLocked) {
690 doInsertion();
691 } else {
692 MutexAutoLock lock(mInterceptorMapMutex);
693 doInsertion();
696 hr = unkInterceptor->QueryInterface(interceptorIid, aOutInterceptor);
697 ENSURE_HR_SUCCEEDED(hr);
698 return hr;
701 HRESULT
702 Interceptor::QueryInterfaceTarget(REFIID aIid, void** aOutput,
703 TimeDuration* aOutDuration) {
704 // NB: This QI needs to run on the main thread because the target object
705 // is probably Gecko code that is not thread-safe. Note that this main
706 // thread invocation is *synchronous*.
707 if (!NS_IsMainThread() && tlsCreatingStdMarshal.get()) {
708 mStdMarshalMutex.AssertCurrentThreadOwns();
709 // COM queries for special interfaces such as IFastRundown when creating a
710 // marshaler. We don't want these being dispatched to the main thread,
711 // since this would cause a deadlock on mStdMarshalMutex if the main
712 // thread is also querying for IMarshal. If we do need to respond to these
713 // special interfaces, this should be done before this point; e.g. in
714 // Interceptor::QueryInterface like we do for INoMarshal.
715 return E_NOINTERFACE;
718 if (mEventSink->IsInterfaceMaybeSupported(aIid) == E_NOINTERFACE) {
719 return E_NOINTERFACE;
722 MainThreadInvoker invoker;
723 HRESULT hr;
724 auto runOnMainThread = [&]() -> void {
725 MOZ_ASSERT(NS_IsMainThread());
726 hr = mTarget->QueryInterface(aIid, aOutput);
728 if (!invoker.Invoke(NS_NewRunnableFunction("Interceptor::QueryInterface",
729 runOnMainThread))) {
730 return E_FAIL;
732 if (aOutDuration) {
733 *aOutDuration = invoker.GetDuration();
735 return hr;
738 HRESULT
739 Interceptor::QueryInterface(REFIID riid, void** ppv) {
740 if (riid == IID_INoMarshal) {
741 // This entire library is designed around marshaling, so there's no point
742 // propagating this QI request all over the place!
743 return E_NOINTERFACE;
746 return WeakReferenceSupport::QueryInterface(riid, ppv);
749 HRESULT
750 Interceptor::WeakRefQueryInterface(REFIID aIid, IUnknown** aOutInterface) {
751 if (aIid == IID_IStdMarshalInfo) {
752 detail::ReentrySentinel sentinel(this);
754 if (!sentinel.IsOutermost()) {
755 return E_NOINTERFACE;
758 // Do not indicate that this interface is available unless we actually
759 // support it. We'll check that by looking for a successful call to
760 // IInterceptorSink::GetHandler()
761 CLSID dummy;
762 if (FAILED(mEventSink->GetHandler(WrapNotNull(&dummy)))) {
763 return E_NOINTERFACE;
766 RefPtr<IStdMarshalInfo> std(this);
767 std.forget(aOutInterface);
768 return S_OK;
771 if (aIid == IID_IMarshal) {
772 MutexAutoLock lock(mStdMarshalMutex);
774 HRESULT hr;
776 if (!mStdMarshalUnk) {
777 MOZ_ASSERT(!tlsCreatingStdMarshal.get());
778 tlsCreatingStdMarshal.set(true);
779 if (XRE_IsContentProcess()) {
780 hr = FastMarshaler::Create(static_cast<IWeakReferenceSource*>(this),
781 getter_AddRefs(mStdMarshalUnk));
782 } else {
783 hr = ::CoGetStdMarshalEx(static_cast<IWeakReferenceSource*>(this),
784 SMEXF_SERVER, getter_AddRefs(mStdMarshalUnk));
786 tlsCreatingStdMarshal.set(false);
788 ENSURE_HR_SUCCEEDED(hr);
791 if (!mStdMarshal) {
792 hr = mStdMarshalUnk->QueryInterface(IID_IMarshal, (void**)&mStdMarshal);
793 ENSURE_HR_SUCCEEDED(hr);
795 // mStdMarshal is weak, so drop its refcount
796 mStdMarshal->Release();
799 RefPtr<IMarshal> marshal(this);
800 marshal.forget(aOutInterface);
801 return S_OK;
804 if (aIid == IID_IInterceptor) {
805 RefPtr<IInterceptor> intcpt(this);
806 intcpt.forget(aOutInterface);
807 return S_OK;
810 if (aIid == IID_IDispatch) {
811 STAUniquePtr<IDispatch> disp;
812 IDispatch* rawDisp = nullptr;
813 HRESULT hr = QueryInterfaceTarget(aIid, (void**)&rawDisp);
814 ENSURE_HR_SUCCEEDED(hr);
816 disp.reset(rawDisp);
817 return DispatchForwarder::Create(this, disp, aOutInterface);
820 return GetInterceptorForIID(aIid, (void**)aOutInterface, nullptr);
823 ULONG
824 Interceptor::AddRef() { return WeakReferenceSupport::AddRef(); }
826 ULONG
827 Interceptor::Release() { return WeakReferenceSupport::Release(); }
829 /* static */
830 HRESULT Interceptor::DisconnectRemotesForTarget(IUnknown* aTarget) {
831 MOZ_ASSERT(aTarget);
833 detail::LiveSetAutoLock lock(GetLiveSet());
835 // It is not an error if the interceptor doesn't exist, so we return
836 // S_FALSE instead of an error in that case.
837 RefPtr<IWeakReference> existingWeak(GetLiveSet().Get(aTarget));
838 if (!existingWeak) {
839 return S_FALSE;
842 RefPtr<IWeakReferenceSource> existingStrong;
843 if (FAILED(existingWeak->ToStrongRef(getter_AddRefs(existingStrong)))) {
844 return S_FALSE;
846 // Since we now hold a strong ref on the interceptor, we may now release the
847 // lock.
848 lock.Unlock();
850 return ::CoDisconnectObject(existingStrong, 0);
853 } // namespace mscom
854 } // namespace mozilla