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 #if defined(MOZILLA_INTERNAL_API)
8 # include "MainThreadUtils.h"
9 # include "mozilla/dom/ContentChild.h"
12 #if defined(ACCESSIBILITY)
13 # include "mozilla/mscom/Registration.h"
14 # if defined(MOZILLA_INTERNAL_API)
15 # include "nsTArray.h"
19 #include "mozilla/ArrayUtils.h"
20 #include "mozilla/mscom/COMWrappers.h"
21 #include "mozilla/DebugOnly.h"
22 #include "mozilla/mscom/Objref.h"
23 #include "mozilla/mscom/Utils.h"
24 #include "mozilla/RefPtr.h"
25 #include "mozilla/WindowsVersion.h"
34 extern "C" IMAGE_DOS_HEADER __ImageBase
;
40 bool IsCOMInitializedOnCurrentThread() {
42 APTTYPEQUALIFIER aptTypeQualifier
;
43 HRESULT hr
= wrapped::CoGetApartmentType(&aptType
, &aptTypeQualifier
);
44 return hr
!= CO_E_NOTINITIALIZED
;
47 bool IsCurrentThreadMTA() {
49 APTTYPEQUALIFIER aptTypeQualifier
;
50 HRESULT hr
= wrapped::CoGetApartmentType(&aptType
, &aptTypeQualifier
);
55 return aptType
== APTTYPE_MTA
;
58 bool IsCurrentThreadExplicitMTA() {
60 APTTYPEQUALIFIER aptTypeQualifier
;
61 HRESULT hr
= wrapped::CoGetApartmentType(&aptType
, &aptTypeQualifier
);
66 return aptType
== APTTYPE_MTA
&&
67 aptTypeQualifier
!= APTTYPEQUALIFIER_IMPLICIT_MTA
;
70 bool IsCurrentThreadImplicitMTA() {
72 APTTYPEQUALIFIER aptTypeQualifier
;
73 HRESULT hr
= wrapped::CoGetApartmentType(&aptType
, &aptTypeQualifier
);
78 return aptType
== APTTYPE_MTA
&&
79 aptTypeQualifier
== APTTYPEQUALIFIER_IMPLICIT_MTA
;
82 #if defined(MOZILLA_INTERNAL_API)
83 bool IsCurrentThreadNonMainMTA() {
84 if (NS_IsMainThread()) {
88 return IsCurrentThreadMTA();
90 #endif // defined(MOZILLA_INTERNAL_API)
92 bool IsProxy(IUnknown
* aUnknown
) {
97 // Only proxies implement this interface, so if it is present then we must
98 // be dealing with a proxied object.
99 RefPtr
<IClientSecurity
> clientSecurity
;
100 HRESULT hr
= aUnknown
->QueryInterface(IID_IClientSecurity
,
101 (void**)getter_AddRefs(clientSecurity
));
102 if (SUCCEEDED(hr
) || hr
== RPC_E_WRONG_THREAD
) {
108 bool IsValidGUID(REFGUID aCheckGuid
) {
109 // This function determines whether or not aCheckGuid conforms to RFC4122
110 // as it applies to Microsoft COM.
112 BYTE variant
= aCheckGuid
.Data4
[0];
113 if (!(variant
& 0x80)) {
117 if ((variant
& 0xE0) == 0xE0) {
118 // Reserved for future use
121 if ((variant
& 0xC0) == 0xC0) {
122 // Microsoft Reserved.
126 BYTE version
= HIBYTE(aCheckGuid
.Data3
) >> 4;
127 // Other versions are specified in RFC4122 but these are the two used by COM.
128 return version
== 1 || version
== 4;
131 uintptr_t GetContainingModuleHandle() {
132 HMODULE thisModule
= nullptr;
133 #if defined(_MSC_VER)
134 thisModule
= reinterpret_cast<HMODULE
>(&__ImageBase
);
136 if (!GetModuleHandleEx(GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS
|
137 GET_MODULE_HANDLE_EX_FLAG_UNCHANGED_REFCOUNT
,
138 reinterpret_cast<LPCTSTR
>(&GetContainingModuleHandle
),
143 return reinterpret_cast<uintptr_t>(thisModule
);
148 long BuildRegGuidPath(REFGUID aGuid
, const GuidType aGuidType
, wchar_t* aBuf
,
149 const size_t aBufLen
) {
150 constexpr wchar_t kClsid
[] = L
"CLSID\\";
151 constexpr wchar_t kAppid
[] = L
"AppID\\";
152 constexpr wchar_t kSubkeyBase
[] = L
"SOFTWARE\\Classes\\";
154 // We exclude null terminators in these length calculations because we include
155 // the stringified GUID's null terminator at the end. Since kClsid and kAppid
156 // have identical lengths, we just choose one to compute this length.
157 constexpr size_t kSubkeyBaseLen
= mozilla::ArrayLength(kSubkeyBase
) - 1;
158 constexpr size_t kSubkeyLen
=
159 kSubkeyBaseLen
+ mozilla::ArrayLength(kClsid
) - 1;
160 // Guid length as formatted for the registry (including curlies and dashes),
161 // but excluding null terminator.
162 constexpr size_t kGuidLen
= kGuidRegFormatCharLenInclNul
- 1;
163 constexpr size_t kExpectedPathLenInclNul
= kSubkeyLen
+ kGuidLen
+ 1;
165 if (aBufLen
< kExpectedPathLenInclNul
) {
166 // Buffer is too short
170 if (wcscpy_s(aBuf
, aBufLen
, kSubkeyBase
)) {
174 const wchar_t* strGuidType
= aGuidType
== GuidType::CLSID
? kClsid
: kAppid
;
175 if (wcscat_s(aBuf
, aBufLen
, strGuidType
)) {
179 int guidConversionResult
=
180 ::StringFromGUID2(aGuid
, &aBuf
[kSubkeyLen
], aBufLen
- kSubkeyLen
);
181 if (!guidConversionResult
) {
188 } // namespace detail
190 long CreateStream(const uint8_t* aInitBuf
, const uint32_t aInitBufSize
,
191 IStream
** aOutStream
) {
192 if (!aInitBufSize
|| !aOutStream
) {
196 *aOutStream
= nullptr;
199 RefPtr
<IStream
> stream
;
201 if (IsWin8OrLater()) {
202 // SHCreateMemStream is not safe for us to use until Windows 8. On older
203 // versions of Windows it is not thread-safe and it creates IStreams that do
204 // not support the full IStream API.
206 // If aInitBuf is null then initSize must be 0.
207 UINT initSize
= aInitBuf
? aInitBufSize
: 0;
208 stream
= already_AddRefed
<IStream
>(::SHCreateMemStream(aInitBuf
, initSize
));
210 return E_OUTOFMEMORY
;
214 // Now we'll set the required size
215 ULARGE_INTEGER newSize
;
216 newSize
.QuadPart
= aInitBufSize
;
217 hr
= stream
->SetSize(newSize
);
223 HGLOBAL hglobal
= ::GlobalAlloc(GMEM_MOVEABLE
, aInitBufSize
);
225 return HRESULT_FROM_WIN32(::GetLastError());
228 // stream takes ownership of hglobal if this call is successful
229 hr
= ::CreateStreamOnHGlobal(hglobal
, TRUE
, getter_AddRefs(stream
));
231 ::GlobalFree(hglobal
);
235 // The default stream size is derived from ::GlobalSize(hglobal), which due
236 // to rounding may be larger than aInitBufSize. We forcibly set the correct
238 ULARGE_INTEGER streamSize
;
239 streamSize
.QuadPart
= aInitBufSize
;
240 hr
= stream
->SetSize(streamSize
);
247 hr
= stream
->Write(aInitBuf
, aInitBufSize
, &bytesWritten
);
252 if (bytesWritten
!= aInitBufSize
) {
258 // Ensure that the stream is rewound
259 LARGE_INTEGER streamOffset
;
260 streamOffset
.QuadPart
= 0LL;
261 hr
= stream
->Seek(streamOffset
, STREAM_SEEK_SET
, nullptr);
266 stream
.forget(aOutStream
);
270 long CopySerializedProxy(IStream
* aInStream
, IStream
** aOutStream
) {
271 if (!aInStream
|| !aOutStream
) {
275 *aOutStream
= nullptr;
277 uint32_t desiredStreamSize
= GetOBJREFSize(WrapNotNull(aInStream
));
278 if (!desiredStreamSize
) {
282 RefPtr
<IStream
> stream
;
283 HRESULT hr
= CreateStream(nullptr, desiredStreamSize
, getter_AddRefs(stream
));
288 ULARGE_INTEGER numBytesToCopy
;
289 numBytesToCopy
.QuadPart
= desiredStreamSize
;
290 hr
= aInStream
->CopyTo(stream
, numBytesToCopy
, nullptr, nullptr);
295 LARGE_INTEGER seekTo
;
296 seekTo
.QuadPart
= 0LL;
297 hr
= stream
->Seek(seekTo
, STREAM_SEEK_SET
, nullptr);
302 stream
.forget(aOutStream
);
306 #if defined(MOZILLA_INTERNAL_API)
308 void GUIDToString(REFGUID aGuid
, nsAString
& aOutString
) {
309 // This buffer length is long enough to hold a GUID string that is formatted
310 // to include curly braces and dashes.
311 const int kBufLenWithNul
= 39;
312 aOutString
.SetLength(kBufLenWithNul
);
313 int result
= StringFromGUID2(aGuid
, char16ptr_t(aOutString
.BeginWriting()),
317 // Truncate the terminator
318 aOutString
.SetLength(result
- 1);
322 // Undocumented IIDs that are relevant for diagnostic purposes
323 static const IID IID_ISCMLocalActivator
= {
327 {0xC0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x46}};
328 static const IID IID_IRundown
= {
332 {0xC0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x46}};
333 static const IID IID_IRemUnknown
= {
337 {0xC0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x46}};
338 static const IID IID_IRemUnknown2
= {
342 {0xC0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x46}};
344 struct IIDToLiteralMapEntry
{
345 constexpr IIDToLiteralMapEntry(REFIID aIid
, nsLiteralCString
&& aStr
)
346 : mIid(aIid
), mStr(std::forward
<nsLiteralCString
>(aStr
)) {}
349 const nsLiteralCString mStr
;
353 * Given the name of an interface, the IID_ENTRY macro generates a pair
354 * containing a reference to the interface ID and a stringified version of
355 * the interface name.
359 * {IID_ENTRY(IUnknown)}
361 * {IID_IUnknown, "IUnknown"_ns}
365 # define IID_ENTRY_STRINGIFY(iface) #iface##_ns
366 # define IID_ENTRY(iface) IID_##iface, IID_ENTRY_STRINGIFY(iface)
369 // Mapping of selected IIDs to friendly, human readable descriptions for each
371 static constexpr IIDToLiteralMapEntry sIidDiagStrs
[] = {
372 {IID_ENTRY(IUnknown
)},
373 {IID_IRemUnknown
, "cross-apartment IUnknown"_ns
},
374 {IID_IRundown
, "cross-apartment object management"_ns
},
375 {IID_ISCMLocalActivator
, "out-of-process object instantiation"_ns
},
376 {IID_IRemUnknown2
, "cross-apartment IUnknown"_ns
}};
379 # undef IID_ENTRY_STRINGIFY
381 void DiagnosticNameForIID(REFIID aIid
, nsACString
& aOutString
) {
382 // If the IID matches something in sIidDiagStrs, output its string.
383 for (const auto& curEntry
: sIidDiagStrs
) {
384 if (curEntry
.mIid
== aIid
) {
385 aOutString
.Assign(curEntry
.mStr
);
390 // Otherwise just convert the IID to string form and output that.
392 GUIDToString(aIid
, strIid
);
394 aOutString
.AssignLiteral("IID ");
395 AppendUTF16toUTF8(strIid
, aOutString
);
400 void GUIDToString(REFGUID aGuid
,
401 wchar_t (&aOutBuf
)[kGuidRegFormatCharLenInclNul
]) {
402 DebugOnly
<int> result
=
403 ::StringFromGUID2(aGuid
, aOutBuf
, ArrayLength(aOutBuf
));
407 #endif // defined(MOZILLA_INTERNAL_API)
409 #if defined(ACCESSIBILITY)
411 static bool IsVtableIndexFromParentInterface(TYPEATTR
* aTypeAttr
,
412 unsigned long aVtableIndex
) {
413 MOZ_ASSERT(aTypeAttr
);
415 // This is the number of functions declared in this interface (excluding
416 // parent interfaces).
417 unsigned int numExclusiveFuncs
= aTypeAttr
->cFuncs
;
419 // This is the number of vtable entries (which includes parent interfaces).
420 // TYPEATTR::cbSizeVft is the entire vtable size in bytes, so we need to
421 // divide in order to compute the number of entries.
422 unsigned int numVtblEntries
= aTypeAttr
->cbSizeVft
/ sizeof(void*);
424 // This is the index of the first entry in the vtable that belongs to this
425 // interface and not a parent.
426 unsigned int firstVtblIndex
= numVtblEntries
- numExclusiveFuncs
;
428 // If aVtableIndex is less than firstVtblIndex, then we're asking for an
429 // index that may belong to a parent interface.
430 return aVtableIndex
< firstVtblIndex
;
433 bool IsVtableIndexFromParentInterface(REFIID aInterface
,
434 unsigned long aVtableIndex
) {
435 RefPtr
<ITypeInfo
> typeInfo
;
436 if (!RegisteredProxy::Find(aInterface
, getter_AddRefs(typeInfo
))) {
440 TYPEATTR
* typeAttr
= nullptr;
441 HRESULT hr
= typeInfo
->GetTypeAttr(&typeAttr
);
446 bool result
= IsVtableIndexFromParentInterface(typeAttr
, aVtableIndex
);
448 typeInfo
->ReleaseTypeAttr(typeAttr
);
452 # if defined(MOZILLA_INTERNAL_API)
454 bool IsCallerExternalProcess() {
455 MOZ_ASSERT(XRE_IsContentProcess());
458 * CoGetCallerTID() gives us the caller's thread ID when that thread resides
459 * in a single-threaded apartment. Since our chrome main thread does live
460 * inside an STA, we will therefore be able to check whether the caller TID
461 * equals our chrome main thread TID. This enables us to distinguish
462 * between our chrome thread vs other out-of-process callers. We check for
463 * S_FALSE to ensure that the caller is a different process from ours, which
464 * is the only scenario that we care about.
467 if (::CoGetCallerTID(&callerTid
) != S_FALSE
) {
471 // Now check whether the caller is our parent process main thread.
472 const DWORD parentMainTid
=
473 dom::ContentChild::GetSingleton()->GetChromeMainThreadId();
474 return callerTid
!= parentMainTid
;
477 bool IsInterfaceEqualToOrInheritedFrom(REFIID aInterface
, REFIID aFrom
,
478 unsigned long aVtableIndexHint
) {
479 if (aInterface
== aFrom
) {
483 // We expect this array to be length 1 but that is not guaranteed by the API.
484 AutoTArray
<RefPtr
<ITypeInfo
>, 1> typeInfos
;
486 // Grab aInterface's ITypeInfo so that we may obtain information about its
487 // inheritance hierarchy.
488 RefPtr
<ITypeInfo
> typeInfo
;
489 if (RegisteredProxy::Find(aInterface
, getter_AddRefs(typeInfo
))) {
490 typeInfos
.AppendElement(std::move(typeInfo
));
494 * The main loop of this function searches the hierarchy of aInterface's
495 * parent interfaces, searching for aFrom.
497 while (!typeInfos
.IsEmpty()) {
498 RefPtr
<ITypeInfo
> curTypeInfo(typeInfos
.PopLastElement());
500 TYPEATTR
* typeAttr
= nullptr;
501 HRESULT hr
= curTypeInfo
->GetTypeAttr(&typeAttr
);
506 bool isFromParentVtable
=
507 IsVtableIndexFromParentInterface(typeAttr
, aVtableIndexHint
);
508 WORD numParentInterfaces
= typeAttr
->cImplTypes
;
510 curTypeInfo
->ReleaseTypeAttr(typeAttr
);
513 if (!isFromParentVtable
) {
514 // The vtable index cannot belong to this interface (otherwise the IIDs
515 // would already have matched and we would have returned true). Since we
516 // now also know that the vtable index cannot possibly be contained inside
517 // curTypeInfo's parent interface, there is no point searching any further
518 // up the hierarchy from here. OTOH we still should check any remaining
519 // entries that are still in the typeInfos array, so we continue.
523 for (WORD i
= 0; i
< numParentInterfaces
; ++i
) {
525 hr
= curTypeInfo
->GetRefTypeOfImplType(i
, &refCookie
);
530 RefPtr
<ITypeInfo
> nextTypeInfo
;
531 hr
= curTypeInfo
->GetRefTypeInfo(refCookie
, getter_AddRefs(nextTypeInfo
));
536 hr
= nextTypeInfo
->GetTypeAttr(&typeAttr
);
541 IID nextIid
= typeAttr
->guid
;
543 nextTypeInfo
->ReleaseTypeAttr(typeAttr
);
546 if (nextIid
== aFrom
) {
550 typeInfos
.AppendElement(std::move(nextTypeInfo
));
557 # endif // defined(MOZILLA_INTERNAL_API)
559 #endif // defined(ACCESSIBILITY)
561 #if defined(MOZILLA_INTERNAL_API)
562 bool IsClassThreadAwareInprocServer(REFCLSID aClsid
) {
563 nsAutoString strClsid
;
564 GUIDToString(aClsid
, strClsid
);
566 nsAutoString
inprocServerSubkey(u
"CLSID\\"_ns
);
567 inprocServerSubkey
.Append(strClsid
);
568 inprocServerSubkey
.Append(u
"\\InprocServer32"_ns
);
570 // Of the possible values, "Apartment" is the longest, so we'll make this
571 // buffer large enough to hold that one.
572 wchar_t threadingModelBuf
[ArrayLength(L
"Apartment")] = {};
574 DWORD numBytes
= sizeof(threadingModelBuf
);
575 LONG result
= ::RegGetValueW(HKEY_CLASSES_ROOT
, inprocServerSubkey
.get(),
576 L
"ThreadingModel", RRF_RT_REG_SZ
, nullptr,
577 threadingModelBuf
, &numBytes
);
578 if (result
!= ERROR_SUCCESS
) {
579 // This will also handle the case where the CLSID is not an inproc server.
583 DWORD numChars
= numBytes
/ sizeof(wchar_t);
584 // numChars includes the null terminator
589 nsDependentString
threadingModel(threadingModelBuf
, numChars
- 1);
591 // Ensure that the threading model is one of the known values that indicates
592 // that the class can operate natively (ie, no proxying) inside a MTA.
593 return threadingModel
.LowerCaseEqualsLiteral("both") ||
594 threadingModel
.LowerCaseEqualsLiteral("free") ||
595 threadingModel
.LowerCaseEqualsLiteral("neutral");
597 #endif // defined(MOZILLA_INTERNAL_API)
600 } // namespace mozilla