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/. */
9 #include "mozilla/mscom/Interceptor.h"
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)) { \
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
));
61 void Remove(IUnknown
* aKey
) {
62 mMutex
.AssertCurrentThreadOwns();
63 mLiveSet
.Remove(aKey
);
67 Mutex mMutex MOZ_UNANNOTATED
;
68 nsRefPtrHashtable
<nsPtrHashKey
<IUnknown
>, IWeakReference
> mLiveSet
;
72 * We don't use the normal XPCOM BaseAutoLock because we need the ability
73 * to explicitly Unlock.
75 class MOZ_RAII LiveSetAutoLock final
{
77 explicit LiveSetAutoLock(LiveSet
& aLiveSet
) : mLiveSet(&aLiveSet
) {
95 LiveSetAutoLock(const LiveSetAutoLock
& aOther
) = delete;
96 LiveSetAutoLock(LiveSetAutoLock
&& aOther
) = delete;
97 LiveSetAutoLock
& operator=(const LiveSetAutoLock
& aOther
) = delete;
98 LiveSetAutoLock
& operator=(LiveSetAutoLock
&& aOther
) = delete;
104 class MOZ_RAII ReentrySentinel final
{
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;
126 bool IsMarshaling(Interceptor
* aTopInterceptor
) const {
127 return aTopInterceptor
== mCurInterceptor
||
128 (mPrevSentinel
&& mPrevSentinel
->IsMarshaling(aTopInterceptor
));
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
{
142 explicit LoggedQIResult(REFIID aIid
)
146 mInterceptor(nullptr),
147 mBegin(TimeStamp::Now()) {}
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
) {
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;
182 IUnknown
* mInterceptor
;
183 TimeDuration mNonOverheadDuration
;
187 } // namespace detail
189 static detail::LiveSet
& GetLiveSet() {
190 static detail::LiveSet sLiveSet
;
194 MOZ_THREAD_LOCAL(bool) Interceptor::tlsCreatingStdMarshal
;
197 HRESULT
Interceptor::Create(STAUniquePtr
<IUnknown
> aTarget
,
198 IInterceptorSink
* aSink
, REFIID aInitialIid
,
199 void** aOutInterface
) {
200 MOZ_ASSERT(aOutInterface
&& aTarget
&& aSink
);
201 if (!aOutInterface
) {
205 detail::LiveSetAutoLock
lock(GetLiveSet());
207 RefPtr
<IWeakReference
> existingWeak(GetLiveSet().Get(aTarget
.get()));
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.
214 return existingStrong
->QueryInterface(aInitialIid
, aOutInterface
);
218 *aOutInterface
= nullptr;
220 if (!aTarget
|| !aSink
) {
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
),
232 mInterceptorMapMutex("mozilla::mscom::Interceptor::mInterceptorMapMutex"),
233 mStdMarshalMutex("mozilla::mscom::Interceptor::mStdMarshalMutex"),
234 mStdMarshal(nullptr) {
235 static const bool kHasTls
= tlsCreatingStdMarshal
.init();
240 RefPtr
<IWeakReference
> weakRef
;
241 if (SUCCEEDED(GetWeakReference(getter_AddRefs(weakRef
)))) {
242 aSink
->SetInterceptor(weakRef
);
246 Interceptor::~Interceptor() {
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
;
257 MapEntry
& entry
= mInterceptorMap
[index
];
258 entry
.mInterceptor
= nullptr;
259 entry
.mTargetInterface
->Release();
264 Interceptor::GetClassForHandler(DWORD aDestContext
, void* aDestContextPtr
,
265 CLSID
* aHandlerClsid
) {
266 if (aDestContextPtr
|| !aHandlerClsid
||
267 aDestContext
== MSHCTX_DIFFERENTMACHINE
) {
271 MOZ_ASSERT(mEventSink
);
272 return mEventSink
->GetHandler(WrapNotNull(aHandlerClsid
));
276 Interceptor::MarshalAs(REFIID aIid
) const {
277 #if defined(MOZ_MSCOM_REMARSHAL_NO_HANDLER)
278 return IsCallerExternalProcess() ? aIid
: mEventSink
->MarshalAs(aIid
);
280 return mEventSink
->MarshalAs(aIid
);
281 #endif // defined(MOZ_MSCOM_REMARSHAL_NO_HANDLER)
285 Interceptor::GetUnmarshalClass(REFIID riid
, void* pv
, DWORD dwDestContext
,
286 void* pvDestContext
, DWORD mshlflags
,
288 return mStdMarshal
->GetUnmarshalClass(MarshalAs(riid
), pv
, dwDestContext
,
289 pvDestContext
, mshlflags
, pCid
);
293 Interceptor::GetMarshalSizeMax(REFIID riid
, void* pv
, DWORD dwDestContext
,
294 void* pvDestContext
, DWORD mshlflags
,
296 detail::ReentrySentinel
sentinel(this);
298 HRESULT hr
= mStdMarshal
->GetMarshalSizeMax(
299 MarshalAs(riid
), pv
, dwDestContext
, pvDestContext
, mshlflags
, pSize
);
300 if (FAILED(hr
) || !sentinel
.IsOutermost()) {
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.
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
) {
321 *pSize
+= payloadSize
;
327 Interceptor::MarshalInterface(IStream
* pStm
, REFIID riid
, void* pv
,
328 DWORD dwDestContext
, void* pvDestContext
,
330 detail::ReentrySentinel
sentinel(this);
334 #if defined(MOZ_MSCOM_REMARSHAL_NO_HANDLER)
335 // Save the current stream position
336 LARGE_INTEGER seekTo
;
339 ULARGE_INTEGER objrefPos
;
341 hr
= pStm
->Seek(seekTo
, STREAM_SEEK_CUR
, &objrefPos
);
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()) {
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
);
366 // Now strip out the handler.
367 if (!StripHandlerFromOBJREF(WrapNotNull(pStm
), objrefPos
.QuadPart
,
374 #endif // defined(MOZ_MSCOM_REMARSHAL_NO_HANDLER)
376 hr
= mEventSink
->WriteHandlerPayload(WrapNotNull(this), WrapNotNull(pStm
));
377 if (hr
== E_NOTIMPL
) {
385 Interceptor::UnmarshalInterface(IStream
* pStm
, REFIID riid
, void** ppv
) {
386 return mStdMarshal
->UnmarshalInterface(pStm
, riid
, ppv
);
390 Interceptor::ReleaseMarshalData(IStream
* pStm
) {
391 return mStdMarshal
->ReleaseMarshalData(pStm
);
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
;
405 if (mInterceptorMap
[index
].mIID
== aIid
) {
406 return &mInterceptorMap
[index
];
413 Interceptor::GetTargetForIID(REFIID aIid
,
414 InterceptorTargetPtr
<IUnknown
>& aTarget
) {
415 MutexAutoLock
lock(mInterceptorMapMutex
);
416 MapEntry
* entry
= Lookup(aIid
);
418 aTarget
.reset(entry
->mTargetInterface
);
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
428 static const HRESULT kFileNotFound
= HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND
);
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
) {
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.
452 return kFileNotFound
;
455 hr
= ::CoGetInterceptorFromTypeInfo(aIid
, aOuter
, typeInfo
, IID_IUnknown
,
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
));
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
));
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();
491 Interceptor::GetInitialInterceptorForIID(detail::LiveSetAutoLock
& aLiveSetLock
,
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
,
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
);
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
,
543 ENSURE_HR_SUCCEEDED(hr
);
545 if (MarshalAs(aTargetIid
) == aTargetIid
) {
546 hr
= unkInterceptor
->QueryInterface(aTargetIid
, aOutInterceptor
);
547 ENSURE_HR_SUCCEEDED(hr
);
551 hr
= GetInterceptorForIID(aTargetIid
, aOutInterceptor
, &lock
);
552 ENSURE_HR_SUCCEEDED(hr
);
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|,
572 Interceptor::GetInterceptorForIID(REFIID aIid
, void** aOutInterceptor
,
573 MutexAutoLock
* aAlreadyLocked
) {
574 detail::LoggedQIResult
result(aIid
);
576 if (!aOutInterceptor
) {
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
);
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
594 auto doLookup
= [&]() -> void {
595 MapEntry
* entry
= Lookup(interceptorIid
);
597 unkInterceptor
= entry
->mInterceptor
;
598 interfaceForQILog
= entry
->mTargetInterface
;
602 if (aAlreadyLocked
) {
605 MutexAutoLock
lock(mInterceptorMapMutex
);
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
615 result
.Log(mTarget
.get(), interfaceForQILog
);
616 result
= unkInterceptor
->QueryInterface(interceptorIid
, aOutInterceptor
);
617 ENSURE_HR_SUCCEEDED(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!
629 STAUniquePtr
<IUnknown
> targetInterface
;
630 IUnknown
* rawTargetInterface
= nullptr;
632 QueryInterfaceTarget(interceptorIid
, (void**)&rawTargetInterface
, result
);
633 targetInterface
.reset(rawTargetInterface
);
635 result
.Log(mTarget
.get(), targetInterface
.get());
636 MOZ_ASSERT(SUCCEEDED(hr
) || hr
== E_NOINTERFACE
);
637 if (hr
== E_NOINTERFACE
) {
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
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
;
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
) {
692 MutexAutoLock
lock(mInterceptorMapMutex
);
696 hr
= unkInterceptor
->QueryInterface(interceptorIid
, aOutInterceptor
);
697 ENSURE_HR_SUCCEEDED(hr
);
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
;
724 auto runOnMainThread
= [&]() -> void {
725 MOZ_ASSERT(NS_IsMainThread());
726 hr
= mTarget
->QueryInterface(aIid
, aOutput
);
728 if (!invoker
.Invoke(NS_NewRunnableFunction("Interceptor::QueryInterface",
733 *aOutDuration
= invoker
.GetDuration();
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
);
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()
762 if (FAILED(mEventSink
->GetHandler(WrapNotNull(&dummy
)))) {
763 return E_NOINTERFACE
;
766 RefPtr
<IStdMarshalInfo
> std(this);
767 std
.forget(aOutInterface
);
771 if (aIid
== IID_IMarshal
) {
772 MutexAutoLock
lock(mStdMarshalMutex
);
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
));
783 hr
= ::CoGetStdMarshalEx(static_cast<IWeakReferenceSource
*>(this),
784 SMEXF_SERVER
, getter_AddRefs(mStdMarshalUnk
));
786 tlsCreatingStdMarshal
.set(false);
788 ENSURE_HR_SUCCEEDED(hr
);
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
);
804 if (aIid
== IID_IInterceptor
) {
805 RefPtr
<IInterceptor
> intcpt(this);
806 intcpt
.forget(aOutInterface
);
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
);
817 return DispatchForwarder::Create(this, disp
, aOutInterface
);
820 return GetInterceptorForIID(aIid
, (void**)aOutInterface
, nullptr);
824 Interceptor::AddRef() { return WeakReferenceSupport::AddRef(); }
827 Interceptor::Release() { return WeakReferenceSupport::Release(); }
830 HRESULT
Interceptor::DisconnectRemotesForTarget(IUnknown
* 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
));
842 RefPtr
<IWeakReferenceSource
> existingStrong
;
843 if (FAILED(existingWeak
->ToStrongRef(getter_AddRefs(existingStrong
)))) {
846 // Since we now hold a strong ref on the interceptor, we may now release the
850 return ::CoDisconnectObject(existingStrong
, 0);
854 } // namespace mozilla