Bug 1650988 [wpt PR 24478] - Make system color keywords compute to themselves, a...
[gecko.git] / ipc / mscom / Utils.cpp
blobab6ac719696bb4d25301c58ed68761ce8e6093b8
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 "mozilla/dom/ContentChild.h"
9 #endif
11 #if defined(ACCESSIBILITY)
12 # include "mozilla/mscom/Registration.h"
13 # if defined(MOZILLA_INTERNAL_API)
14 # include "nsTArray.h"
15 # endif
16 #endif
18 #include "mozilla/mscom/Objref.h"
19 #include "mozilla/mscom/Utils.h"
20 #include "mozilla/RefPtr.h"
21 #include "mozilla/WindowsVersion.h"
23 #include <objbase.h>
24 #include <objidl.h>
25 #include <shlwapi.h>
26 #include <winnt.h>
28 #if defined(_MSC_VER)
29 extern "C" IMAGE_DOS_HEADER __ImageBase;
30 #endif
32 namespace mozilla {
33 namespace mscom {
35 bool IsCOMInitializedOnCurrentThread() {
36 APTTYPE aptType;
37 APTTYPEQUALIFIER aptTypeQualifier;
38 HRESULT hr = CoGetApartmentType(&aptType, &aptTypeQualifier);
39 return hr != CO_E_NOTINITIALIZED;
42 bool IsCurrentThreadMTA() {
43 APTTYPE aptType;
44 APTTYPEQUALIFIER aptTypeQualifier;
45 HRESULT hr = CoGetApartmentType(&aptType, &aptTypeQualifier);
46 if (FAILED(hr)) {
47 return false;
50 return aptType == APTTYPE_MTA;
53 bool IsCurrentThreadExplicitMTA() {
54 APTTYPE aptType;
55 APTTYPEQUALIFIER aptTypeQualifier;
56 HRESULT hr = CoGetApartmentType(&aptType, &aptTypeQualifier);
57 if (FAILED(hr)) {
58 return false;
61 return aptType == APTTYPE_MTA &&
62 aptTypeQualifier != APTTYPEQUALIFIER_IMPLICIT_MTA;
65 bool IsCurrentThreadImplicitMTA() {
66 APTTYPE aptType;
67 APTTYPEQUALIFIER aptTypeQualifier;
68 HRESULT hr = CoGetApartmentType(&aptType, &aptTypeQualifier);
69 if (FAILED(hr)) {
70 return false;
73 return aptType == APTTYPE_MTA &&
74 aptTypeQualifier == APTTYPEQUALIFIER_IMPLICIT_MTA;
77 bool IsProxy(IUnknown* aUnknown) {
78 if (!aUnknown) {
79 return false;
82 // Only proxies implement this interface, so if it is present then we must
83 // be dealing with a proxied object.
84 RefPtr<IClientSecurity> clientSecurity;
85 HRESULT hr = aUnknown->QueryInterface(IID_IClientSecurity,
86 (void**)getter_AddRefs(clientSecurity));
87 if (SUCCEEDED(hr) || hr == RPC_E_WRONG_THREAD) {
88 return true;
90 return false;
93 bool IsValidGUID(REFGUID aCheckGuid) {
94 // This function determines whether or not aCheckGuid conforms to RFC4122
95 // as it applies to Microsoft COM.
97 BYTE variant = aCheckGuid.Data4[0];
98 if (!(variant & 0x80)) {
99 // NCS Reserved
100 return false;
102 if ((variant & 0xE0) == 0xE0) {
103 // Reserved for future use
104 return false;
106 if ((variant & 0xC0) == 0xC0) {
107 // Microsoft Reserved.
108 return true;
111 BYTE version = HIBYTE(aCheckGuid.Data3) >> 4;
112 // Other versions are specified in RFC4122 but these are the two used by COM.
113 return version == 1 || version == 4;
116 uintptr_t GetContainingModuleHandle() {
117 HMODULE thisModule = nullptr;
118 #if defined(_MSC_VER)
119 thisModule = reinterpret_cast<HMODULE>(&__ImageBase);
120 #else
121 if (!GetModuleHandleEx(GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS |
122 GET_MODULE_HANDLE_EX_FLAG_UNCHANGED_REFCOUNT,
123 reinterpret_cast<LPCTSTR>(&GetContainingModuleHandle),
124 &thisModule)) {
125 return 0;
127 #endif
128 return reinterpret_cast<uintptr_t>(thisModule);
131 uint32_t CreateStream(const uint8_t* aInitBuf, const uint32_t aInitBufSize,
132 IStream** aOutStream) {
133 if (!aInitBufSize || !aOutStream) {
134 return E_INVALIDARG;
137 *aOutStream = nullptr;
139 HRESULT hr;
140 RefPtr<IStream> stream;
142 if (IsWin8OrLater()) {
143 // SHCreateMemStream is not safe for us to use until Windows 8. On older
144 // versions of Windows it is not thread-safe and it creates IStreams that do
145 // not support the full IStream API.
147 // If aInitBuf is null then initSize must be 0.
148 UINT initSize = aInitBuf ? aInitBufSize : 0;
149 stream = already_AddRefed<IStream>(::SHCreateMemStream(aInitBuf, initSize));
150 if (!stream) {
151 return E_OUTOFMEMORY;
154 if (!aInitBuf) {
155 // Now we'll set the required size
156 ULARGE_INTEGER newSize;
157 newSize.QuadPart = aInitBufSize;
158 hr = stream->SetSize(newSize);
159 if (FAILED(hr)) {
160 return hr;
163 } else {
164 HGLOBAL hglobal = ::GlobalAlloc(GMEM_MOVEABLE, aInitBufSize);
165 if (!hglobal) {
166 return HRESULT_FROM_WIN32(::GetLastError());
169 // stream takes ownership of hglobal if this call is successful
170 hr = ::CreateStreamOnHGlobal(hglobal, TRUE, getter_AddRefs(stream));
171 if (FAILED(hr)) {
172 ::GlobalFree(hglobal);
173 return hr;
176 // The default stream size is derived from ::GlobalSize(hglobal), which due
177 // to rounding may be larger than aInitBufSize. We forcibly set the correct
178 // stream size here.
179 ULARGE_INTEGER streamSize;
180 streamSize.QuadPart = aInitBufSize;
181 hr = stream->SetSize(streamSize);
182 if (FAILED(hr)) {
183 return hr;
186 if (aInitBuf) {
187 ULONG bytesWritten;
188 hr = stream->Write(aInitBuf, aInitBufSize, &bytesWritten);
189 if (FAILED(hr)) {
190 return hr;
193 if (bytesWritten != aInitBufSize) {
194 return E_UNEXPECTED;
199 // Ensure that the stream is rewound
200 LARGE_INTEGER streamOffset;
201 streamOffset.QuadPart = 0LL;
202 hr = stream->Seek(streamOffset, STREAM_SEEK_SET, nullptr);
203 if (FAILED(hr)) {
204 return hr;
207 stream.forget(aOutStream);
208 return S_OK;
211 uint32_t CopySerializedProxy(IStream* aInStream, IStream** aOutStream) {
212 if (!aInStream || !aOutStream) {
213 return E_INVALIDARG;
216 *aOutStream = nullptr;
218 uint32_t desiredStreamSize = GetOBJREFSize(WrapNotNull(aInStream));
219 if (!desiredStreamSize) {
220 return E_INVALIDARG;
223 RefPtr<IStream> stream;
224 HRESULT hr = CreateStream(nullptr, desiredStreamSize, getter_AddRefs(stream));
225 if (FAILED(hr)) {
226 return hr;
229 ULARGE_INTEGER numBytesToCopy;
230 numBytesToCopy.QuadPart = desiredStreamSize;
231 hr = aInStream->CopyTo(stream, numBytesToCopy, nullptr, nullptr);
232 if (FAILED(hr)) {
233 return hr;
236 LARGE_INTEGER seekTo;
237 seekTo.QuadPart = 0LL;
238 hr = stream->Seek(seekTo, STREAM_SEEK_SET, nullptr);
239 if (FAILED(hr)) {
240 return hr;
243 stream.forget(aOutStream);
244 return S_OK;
247 #if defined(MOZILLA_INTERNAL_API)
249 void GUIDToString(REFGUID aGuid, nsAString& aOutString) {
250 // This buffer length is long enough to hold a GUID string that is formatted
251 // to include curly braces and dashes.
252 const int kBufLenWithNul = 39;
253 aOutString.SetLength(kBufLenWithNul);
254 int result = StringFromGUID2(aGuid, char16ptr_t(aOutString.BeginWriting()),
255 kBufLenWithNul);
256 MOZ_ASSERT(result);
257 if (result) {
258 // Truncate the terminator
259 aOutString.SetLength(result - 1);
263 #endif // defined(MOZILLA_INTERNAL_API)
265 #if defined(ACCESSIBILITY)
267 static bool IsVtableIndexFromParentInterface(TYPEATTR* aTypeAttr,
268 unsigned long aVtableIndex) {
269 MOZ_ASSERT(aTypeAttr);
271 // This is the number of functions declared in this interface (excluding
272 // parent interfaces).
273 unsigned int numExclusiveFuncs = aTypeAttr->cFuncs;
275 // This is the number of vtable entries (which includes parent interfaces).
276 // TYPEATTR::cbSizeVft is the entire vtable size in bytes, so we need to
277 // divide in order to compute the number of entries.
278 unsigned int numVtblEntries = aTypeAttr->cbSizeVft / sizeof(void*);
280 // This is the index of the first entry in the vtable that belongs to this
281 // interface and not a parent.
282 unsigned int firstVtblIndex = numVtblEntries - numExclusiveFuncs;
284 // If aVtableIndex is less than firstVtblIndex, then we're asking for an
285 // index that may belong to a parent interface.
286 return aVtableIndex < firstVtblIndex;
289 bool IsVtableIndexFromParentInterface(REFIID aInterface,
290 unsigned long aVtableIndex) {
291 RefPtr<ITypeInfo> typeInfo;
292 if (!RegisteredProxy::Find(aInterface, getter_AddRefs(typeInfo))) {
293 return false;
296 TYPEATTR* typeAttr = nullptr;
297 HRESULT hr = typeInfo->GetTypeAttr(&typeAttr);
298 if (FAILED(hr)) {
299 return false;
302 bool result = IsVtableIndexFromParentInterface(typeAttr, aVtableIndex);
304 typeInfo->ReleaseTypeAttr(typeAttr);
305 return result;
308 # if defined(MOZILLA_INTERNAL_API)
310 bool IsCallerExternalProcess() {
311 MOZ_ASSERT(XRE_IsContentProcess());
314 * CoGetCallerTID() gives us the caller's thread ID when that thread resides
315 * in a single-threaded apartment. Since our chrome main thread does live
316 * inside an STA, we will therefore be able to check whether the caller TID
317 * equals our chrome main thread TID. This enables us to distinguish
318 * between our chrome thread vs other out-of-process callers. We check for
319 * S_FALSE to ensure that the caller is a different process from ours, which
320 * is the only scenario that we care about.
322 DWORD callerTid;
323 if (::CoGetCallerTID(&callerTid) != S_FALSE) {
324 return false;
327 // Now check whether the caller is our parent process main thread.
328 const DWORD parentMainTid =
329 dom::ContentChild::GetSingleton()->GetChromeMainThreadId();
330 return callerTid != parentMainTid;
333 bool IsInterfaceEqualToOrInheritedFrom(REFIID aInterface, REFIID aFrom,
334 unsigned long aVtableIndexHint) {
335 if (aInterface == aFrom) {
336 return true;
339 // We expect this array to be length 1 but that is not guaranteed by the API.
340 AutoTArray<RefPtr<ITypeInfo>, 1> typeInfos;
342 // Grab aInterface's ITypeInfo so that we may obtain information about its
343 // inheritance hierarchy.
344 RefPtr<ITypeInfo> typeInfo;
345 if (RegisteredProxy::Find(aInterface, getter_AddRefs(typeInfo))) {
346 typeInfos.AppendElement(std::move(typeInfo));
350 * The main loop of this function searches the hierarchy of aInterface's
351 * parent interfaces, searching for aFrom.
353 while (!typeInfos.IsEmpty()) {
354 RefPtr<ITypeInfo> curTypeInfo(typeInfos.PopLastElement());
356 TYPEATTR* typeAttr = nullptr;
357 HRESULT hr = curTypeInfo->GetTypeAttr(&typeAttr);
358 if (FAILED(hr)) {
359 break;
362 bool isFromParentVtable =
363 IsVtableIndexFromParentInterface(typeAttr, aVtableIndexHint);
364 WORD numParentInterfaces = typeAttr->cImplTypes;
366 curTypeInfo->ReleaseTypeAttr(typeAttr);
367 typeAttr = nullptr;
369 if (!isFromParentVtable) {
370 // The vtable index cannot belong to this interface (otherwise the IIDs
371 // would already have matched and we would have returned true). Since we
372 // now also know that the vtable index cannot possibly be contained inside
373 // curTypeInfo's parent interface, there is no point searching any further
374 // up the hierarchy from here. OTOH we still should check any remaining
375 // entries that are still in the typeInfos array, so we continue.
376 continue;
379 for (WORD i = 0; i < numParentInterfaces; ++i) {
380 HREFTYPE refCookie;
381 hr = curTypeInfo->GetRefTypeOfImplType(i, &refCookie);
382 if (FAILED(hr)) {
383 continue;
386 RefPtr<ITypeInfo> nextTypeInfo;
387 hr = curTypeInfo->GetRefTypeInfo(refCookie, getter_AddRefs(nextTypeInfo));
388 if (FAILED(hr)) {
389 continue;
392 hr = nextTypeInfo->GetTypeAttr(&typeAttr);
393 if (FAILED(hr)) {
394 continue;
397 IID nextIid = typeAttr->guid;
399 nextTypeInfo->ReleaseTypeAttr(typeAttr);
400 typeAttr = nullptr;
402 if (nextIid == aFrom) {
403 return true;
406 typeInfos.AppendElement(std::move(nextTypeInfo));
410 return false;
413 # endif // defined(MOZILLA_INTERNAL_API)
415 #endif // defined(ACCESSIBILITY)
417 #if defined(MOZILLA_INTERNAL_API)
418 bool IsClassThreadAwareInprocServer(REFCLSID aClsid) {
419 nsAutoString strClsid;
420 GUIDToString(aClsid, strClsid);
422 nsAutoString inprocServerSubkey(u"CLSID\\"_ns);
423 inprocServerSubkey.Append(strClsid);
424 inprocServerSubkey.Append(u"\\InprocServer32"_ns);
426 // Of the possible values, "Apartment" is the longest, so we'll make this
427 // buffer large enough to hold that one.
428 wchar_t threadingModelBuf[ArrayLength(L"Apartment")] = {};
430 DWORD numBytes = sizeof(threadingModelBuf);
431 LONG result = ::RegGetValueW(HKEY_CLASSES_ROOT, inprocServerSubkey.get(),
432 L"ThreadingModel", RRF_RT_REG_SZ, nullptr,
433 threadingModelBuf, &numBytes);
434 if (result != ERROR_SUCCESS) {
435 // This will also handle the case where the CLSID is not an inproc server.
436 return false;
439 DWORD numChars = numBytes / sizeof(wchar_t);
440 // numChars includes the null terminator
441 if (numChars <= 1) {
442 return false;
445 nsDependentString threadingModel(threadingModelBuf, numChars - 1);
447 // Ensure that the threading model is one of the known values that indicates
448 // that the class can operate natively (ie, no proxying) inside a MTA.
449 return threadingModel.LowerCaseEqualsLiteral("both") ||
450 threadingModel.LowerCaseEqualsLiteral("free") ||
451 threadingModel.LowerCaseEqualsLiteral("neutral");
453 #endif // defined(MOZILLA_INTERNAL_API)
455 } // namespace mscom
456 } // namespace mozilla