1 /* vim: se cin sw=2 ts=2 et : */
2 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-
3 *
4 * This Source Code Form is subject to the terms of the Mozilla Public
5 * License, v. 2.0. If a copy of the MPL was not distributed with this
6 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
7
8 #include "mozilla/ArrayUtils.h"
9
10 #include "GfxInfoBase.h"
11
12 #include "GfxDriverInfo.h"
13 #include "js/Array.h" // JS::GetArrayLength, JS::NewArrayObject
14 #include "nsCOMPtr.h"
15 #include "nsCOMArray.h"
16 #include "nsString.h"
17 #include "nsUnicharUtils.h"
18 #include "nsVersionComparator.h"
19 #include "mozilla/Services.h"
20 #include "mozilla/Observer.h"
21 #include "nsIObserver.h"
22 #include "nsIObserverService.h"
23 #include "nsIScreenManager.h"
24 #include "nsTArray.h"
25 #include "nsXULAppAPI.h"
26 #include "nsIXULAppInfo.h"
27 #include "mozilla/Preferences.h"
28 #include "mozilla/StaticPrefs_gfx.h"
29 #include "mozilla/gfx/2D.h"
30 #include "mozilla/gfx/GPUProcessManager.h"
31 #include "mozilla/gfx/Logging.h"
32 #include "mozilla/gfx/gfxVars.h"
33 #include "mozilla/layers/PaintThread.h"
34
35 #include "gfxPlatform.h"
36 #include "gfxConfig.h"
37 #include "DriverCrashGuard.h"
38
39 using namespace mozilla::widget;
40 using namespace mozilla;
41 using mozilla::MutexAutoLock;
42
43 nsTArray<GfxDriverInfo>* GfxInfoBase::sDriverInfo;
44 nsTArray<dom::GfxInfoFeatureStatus>* GfxInfoBase::sFeatureStatus;
45 bool GfxInfoBase::sDriverInfoObserverInitialized;
46 bool GfxInfoBase::sShutdownOccurred;
47
48 // Observes for shutdown so that the child GfxDriverInfo list is freed.
49 class ShutdownObserver : public nsIObserver {
50 virtual ~ShutdownObserver() = default;
51
52 public:
53 ShutdownObserver() = default;
54
55 NS_DECL_ISUPPORTS
56
Observe(nsISupports * subject,const char * aTopic,const char16_t * aData)57 NS_IMETHOD Observe(nsISupports* subject, const char* aTopic,
58 const char16_t* aData) override {
59 MOZ_ASSERT(strcmp(aTopic, NS_XPCOM_SHUTDOWN_OBSERVER_ID) == 0);
60
61 delete GfxInfoBase::sDriverInfo;
62 GfxInfoBase::sDriverInfo = nullptr;
63
64 delete GfxInfoBase::sFeatureStatus;
65 GfxInfoBase::sFeatureStatus = nullptr;
66
67 for (auto& deviceFamily : GfxDriverInfo::sDeviceFamilies) {
68 delete deviceFamily;
69 deviceFamily = nullptr;
70 }
71
72 for (auto& desktop : GfxDriverInfo::sDesktopEnvironment) {
73 delete desktop;
74 desktop = nullptr;
75 }
76
77 for (auto& windowProtocol : GfxDriverInfo::sWindowProtocol) {
78 delete windowProtocol;
79 windowProtocol = nullptr;
80 }
81
82 for (auto& deviceVendor : GfxDriverInfo::sDeviceVendors) {
83 delete deviceVendor;
84 deviceVendor = nullptr;
85 }
86
87 for (auto& driverVendor : GfxDriverInfo::sDriverVendors) {
88 delete driverVendor;
89 driverVendor = nullptr;
90 }
91
92 GfxInfoBase::sShutdownOccurred = true;
93
94 return NS_OK;
95 }
96 };
97
NS_IMPL_ISUPPORTS(ShutdownObserver,nsIObserver)98 NS_IMPL_ISUPPORTS(ShutdownObserver, nsIObserver)
99
100 static void InitGfxDriverInfoShutdownObserver() {
101 if (GfxInfoBase::sDriverInfoObserverInitialized) return;
102
103 GfxInfoBase::sDriverInfoObserverInitialized = true;
104
105 nsCOMPtr<nsIObserverService> observerService = services::GetObserverService();
106 if (!observerService) {
107 NS_WARNING("Could not get observer service!");
108 return;
109 }
110
111 ShutdownObserver* obs = new ShutdownObserver();
112 observerService->AddObserver(obs, NS_XPCOM_SHUTDOWN_OBSERVER_ID, false);
113 }
114
115 using namespace mozilla::widget;
116 using namespace mozilla::gfx;
117 using namespace mozilla;
118
NS_IMPL_ISUPPORTS(GfxInfoBase,nsIGfxInfo,nsIObserver,nsISupportsWeakReference) const119 NS_IMPL_ISUPPORTS(GfxInfoBase, nsIGfxInfo, nsIObserver,
120 nsISupportsWeakReference)
121
122 #define BLACKLIST_PREF_BRANCH "gfx.blacklist."
123 #define SUGGESTED_VERSION_PREF BLACKLIST_PREF_BRANCH "suggested-driver-version"
124 #define BLACKLIST_ENTRY_TAG_NAME "gfxBlacklistEntry"
125
126 static const char* GetPrefNameForFeature(int32_t aFeature) {
127 const char* name = nullptr;
128 switch (aFeature) {
129 case nsIGfxInfo::FEATURE_DIRECT2D:
130 name = BLACKLIST_PREF_BRANCH "direct2d";
131 break;
132 case nsIGfxInfo::FEATURE_DIRECT3D_9_LAYERS:
133 name = BLACKLIST_PREF_BRANCH "layers.direct3d9";
134 break;
135 case nsIGfxInfo::FEATURE_DIRECT3D_10_LAYERS:
136 name = BLACKLIST_PREF_BRANCH "layers.direct3d10";
137 break;
138 case nsIGfxInfo::FEATURE_DIRECT3D_10_1_LAYERS:
139 name = BLACKLIST_PREF_BRANCH "layers.direct3d10-1";
140 break;
141 case nsIGfxInfo::FEATURE_DIRECT3D_11_LAYERS:
142 name = BLACKLIST_PREF_BRANCH "layers.direct3d11";
143 break;
144 case nsIGfxInfo::FEATURE_DIRECT3D_11_ANGLE:
145 name = BLACKLIST_PREF_BRANCH "direct3d11angle";
146 break;
147 case nsIGfxInfo::FEATURE_HARDWARE_VIDEO_DECODING:
148 name = BLACKLIST_PREF_BRANCH "hardwarevideodecoding";
149 break;
150 case nsIGfxInfo::FEATURE_OPENGL_LAYERS:
151 name = BLACKLIST_PREF_BRANCH "layers.opengl";
152 break;
153 case nsIGfxInfo::FEATURE_WEBGL_OPENGL:
154 name = BLACKLIST_PREF_BRANCH "webgl.opengl";
155 break;
156 case nsIGfxInfo::FEATURE_WEBGL_ANGLE:
157 name = BLACKLIST_PREF_BRANCH "webgl.angle";
158 break;
159 case nsIGfxInfo::FEATURE_WEBGL_MSAA:
160 name = BLACKLIST_PREF_BRANCH "webgl.msaa";
161 break;
162 case nsIGfxInfo::FEATURE_STAGEFRIGHT:
163 name = BLACKLIST_PREF_BRANCH "stagefright";
164 break;
165 case nsIGfxInfo::FEATURE_WEBRTC_HW_ACCELERATION_H264:
166 name = BLACKLIST_PREF_BRANCH "webrtc.hw.acceleration.h264";
167 break;
168 case nsIGfxInfo::FEATURE_WEBRTC_HW_ACCELERATION_ENCODE:
169 name = BLACKLIST_PREF_BRANCH "webrtc.hw.acceleration.encode";
170 break;
171 case nsIGfxInfo::FEATURE_WEBRTC_HW_ACCELERATION_DECODE:
172 name = BLACKLIST_PREF_BRANCH "webrtc.hw.acceleration.decode";
173 break;
174 case nsIGfxInfo::FEATURE_CANVAS2D_ACCELERATION:
175 name = BLACKLIST_PREF_BRANCH "canvas2d.acceleration";
176 break;
177 case nsIGfxInfo::FEATURE_DX_INTEROP2:
178 name = BLACKLIST_PREF_BRANCH "dx.interop2";
179 break;
180 case nsIGfxInfo::FEATURE_GPU_PROCESS:
181 name = BLACKLIST_PREF_BRANCH "gpu.process";
182 break;
183 case nsIGfxInfo::FEATURE_WEBGL2:
184 name = BLACKLIST_PREF_BRANCH "webgl2";
185 break;
186 case nsIGfxInfo::FEATURE_ADVANCED_LAYERS:
187 name = BLACKLIST_PREF_BRANCH "layers.advanced";
188 break;
189 case nsIGfxInfo::FEATURE_D3D11_KEYED_MUTEX:
190 name = BLACKLIST_PREF_BRANCH "d3d11.keyed.mutex";
191 break;
192 case nsIGfxInfo::FEATURE_WEBRENDER:
193 name = BLACKLIST_PREF_BRANCH "webrender";
194 break;
195 case nsIGfxInfo::FEATURE_WEBRENDER_COMPOSITOR:
196 name = BLACKLIST_PREF_BRANCH "webrender.compositor";
197 break;
198 case nsIGfxInfo::FEATURE_DX_NV12:
199 name = BLACKLIST_PREF_BRANCH "dx.nv12";
200 break;
201 case nsIGfxInfo::FEATURE_DX_P010:
202 name = BLACKLIST_PREF_BRANCH "dx.p010";
203 break;
204 case nsIGfxInfo::FEATURE_DX_P016:
205 name = BLACKLIST_PREF_BRANCH "dx.p016";
206 break;
207 case nsIGfxInfo::FEATURE_VP8_HW_DECODE:
208 case nsIGfxInfo::FEATURE_VP9_HW_DECODE:
209 // We don't provide prefs for these features as these are
210 // not handling downloadable blocklist.
211 break;
212 case nsIGfxInfo::FEATURE_GL_SWIZZLE:
213 name = BLACKLIST_PREF_BRANCH "gl.swizzle";
214 break;
215 default:
216 MOZ_ASSERT_UNREACHABLE("Unexpected nsIGfxInfo feature?!");
217 break;
218 }
219
220 return name;
221 }
222
223 // Returns the value of the pref for the relevant feature in aValue.
224 // If the pref doesn't exist, aValue is not touched, and returns false.
GetPrefValueForFeature(int32_t aFeature,int32_t & aValue,nsACString & aFailureId)225 static bool GetPrefValueForFeature(int32_t aFeature, int32_t& aValue,
226 nsACString& aFailureId) {
227 const char* prefname = GetPrefNameForFeature(aFeature);
228 if (!prefname) return false;
229
230 aValue = nsIGfxInfo::FEATURE_STATUS_UNKNOWN;
231 if (!NS_SUCCEEDED(Preferences::GetInt(prefname, &aValue))) {
232 return false;
233 }
234
235 nsCString failureprefname(prefname);
236 failureprefname += ".failureid";
237 nsAutoCString failureValue;
238 nsresult rv = Preferences::GetCString(failureprefname.get(), failureValue);
239 if (NS_SUCCEEDED(rv)) {
240 aFailureId = failureValue.get();
241 } else {
242 aFailureId = "FEATURE_FAILURE_BLACKLIST_PREF";
243 }
244
245 return true;
246 }
247
SetPrefValueForFeature(int32_t aFeature,int32_t aValue,const nsACString & aFailureId)248 static void SetPrefValueForFeature(int32_t aFeature, int32_t aValue,
249 const nsACString& aFailureId) {
250 const char* prefname = GetPrefNameForFeature(aFeature);
251 if (!prefname) return;
252 if (XRE_IsParentProcess()) {
253 delete GfxInfoBase::sFeatureStatus;
254 GfxInfoBase::sFeatureStatus = nullptr;
255 }
256
257 Preferences::SetInt(prefname, aValue);
258 if (!aFailureId.IsEmpty()) {
259 nsCString failureprefname(prefname);
260 failureprefname += ".failureid";
261 Preferences::SetCString(failureprefname.get(), aFailureId);
262 }
263 }
264
RemovePrefForFeature(int32_t aFeature)265 static void RemovePrefForFeature(int32_t aFeature) {
266 const char* prefname = GetPrefNameForFeature(aFeature);
267 if (!prefname) return;
268
269 if (XRE_IsParentProcess()) {
270 delete GfxInfoBase::sFeatureStatus;
271 GfxInfoBase::sFeatureStatus = nullptr;
272 }
273 Preferences::ClearUser(prefname);
274 }
275
GetPrefValueForDriverVersion(nsCString & aVersion)276 static bool GetPrefValueForDriverVersion(nsCString& aVersion) {
277 return NS_SUCCEEDED(
278 Preferences::GetCString(SUGGESTED_VERSION_PREF, aVersion));
279 }
280
SetPrefValueForDriverVersion(const nsAString & aVersion)281 static void SetPrefValueForDriverVersion(const nsAString& aVersion) {
282 Preferences::SetString(SUGGESTED_VERSION_PREF, aVersion);
283 }
284
RemovePrefForDriverVersion()285 static void RemovePrefForDriverVersion() {
286 Preferences::ClearUser(SUGGESTED_VERSION_PREF);
287 }
288
BlacklistOSToOperatingSystem(const nsAString & os)289 static OperatingSystem BlacklistOSToOperatingSystem(const nsAString& os) {
290 if (os.EqualsLiteral("WINNT 6.1"))
291 return OperatingSystem::Windows7;
292 else if (os.EqualsLiteral("WINNT 6.2"))
293 return OperatingSystem::Windows8;
294 else if (os.EqualsLiteral("WINNT 6.3"))
295 return OperatingSystem::Windows8_1;
296 else if (os.EqualsLiteral("WINNT 10.0"))
297 return OperatingSystem::Windows10;
298 else if (os.EqualsLiteral("Linux"))
299 return OperatingSystem::Linux;
300 else if (os.EqualsLiteral("Darwin 9"))
301 return OperatingSystem::OSX10_5;
302 else if (os.EqualsLiteral("Darwin 10"))
303 return OperatingSystem::OSX10_6;
304 else if (os.EqualsLiteral("Darwin 11"))
305 return OperatingSystem::OSX10_7;
306 else if (os.EqualsLiteral("Darwin 12"))
307 return OperatingSystem::OSX10_8;
308 else if (os.EqualsLiteral("Darwin 13"))
309 return OperatingSystem::OSX10_9;
310 else if (os.EqualsLiteral("Darwin 14"))
311 return OperatingSystem::OSX10_10;
312 else if (os.EqualsLiteral("Darwin 15"))
313 return OperatingSystem::OSX10_11;
314 else if (os.EqualsLiteral("Darwin 16"))
315 return OperatingSystem::OSX10_12;
316 else if (os.EqualsLiteral("Darwin 17"))
317 return OperatingSystem::OSX10_13;
318 else if (os.EqualsLiteral("Darwin 18"))
319 return OperatingSystem::OSX10_14;
320 else if (os.EqualsLiteral("Darwin 19"))
321 return OperatingSystem::OSX10_15;
322 else if (os.EqualsLiteral("Android"))
323 return OperatingSystem::Android;
324 // For historical reasons, "All" in blocklist means "All Windows"
325 else if (os.EqualsLiteral("All"))
326 return OperatingSystem::Windows;
327
328 return OperatingSystem::Unknown;
329 }
330
BlacklistDevicesToDeviceFamily(nsTArray<nsCString> & devices)331 static GfxDeviceFamily* BlacklistDevicesToDeviceFamily(
332 nsTArray<nsCString>& devices) {
333 if (devices.Length() == 0) return nullptr;
334
335 // For each device, get its device ID, and return a freshly-allocated
336 // GfxDeviceFamily with the contents of that array.
337 GfxDeviceFamily* deviceIds = new GfxDeviceFamily;
338
339 for (uint32_t i = 0; i < devices.Length(); ++i) {
340 // We make sure we don't add any "empty" device entries to the array, so
341 // we don't need to check if devices[i] is empty.
342 deviceIds->Append(NS_ConvertUTF8toUTF16(devices[i]));
343 }
344
345 return deviceIds;
346 }
347
BlacklistFeatureToGfxFeature(const nsAString & aFeature)348 static int32_t BlacklistFeatureToGfxFeature(const nsAString& aFeature) {
349 MOZ_ASSERT(!aFeature.IsEmpty());
350 if (aFeature.EqualsLiteral("DIRECT2D"))
351 return nsIGfxInfo::FEATURE_DIRECT2D;
352 else if (aFeature.EqualsLiteral("DIRECT3D_9_LAYERS"))
353 return nsIGfxInfo::FEATURE_DIRECT3D_9_LAYERS;
354 else if (aFeature.EqualsLiteral("DIRECT3D_10_LAYERS"))
355 return nsIGfxInfo::FEATURE_DIRECT3D_10_LAYERS;
356 else if (aFeature.EqualsLiteral("DIRECT3D_10_1_LAYERS"))
357 return nsIGfxInfo::FEATURE_DIRECT3D_10_1_LAYERS;
358 else if (aFeature.EqualsLiteral("DIRECT3D_11_LAYERS"))
359 return nsIGfxInfo::FEATURE_DIRECT3D_11_LAYERS;
360 else if (aFeature.EqualsLiteral("DIRECT3D_11_ANGLE"))
361 return nsIGfxInfo::FEATURE_DIRECT3D_11_ANGLE;
362 else if (aFeature.EqualsLiteral("HARDWARE_VIDEO_DECODING"))
363 return nsIGfxInfo::FEATURE_HARDWARE_VIDEO_DECODING;
364 else if (aFeature.EqualsLiteral("OPENGL_LAYERS"))
365 return nsIGfxInfo::FEATURE_OPENGL_LAYERS;
366 else if (aFeature.EqualsLiteral("WEBGL_OPENGL"))
367 return nsIGfxInfo::FEATURE_WEBGL_OPENGL;
368 else if (aFeature.EqualsLiteral("WEBGL_ANGLE"))
369 return nsIGfxInfo::FEATURE_WEBGL_ANGLE;
370 else if (aFeature.EqualsLiteral("WEBGL_MSAA"))
371 return nsIGfxInfo::FEATURE_WEBGL_MSAA;
372 else if (aFeature.EqualsLiteral("STAGEFRIGHT"))
373 return nsIGfxInfo::FEATURE_STAGEFRIGHT;
374 else if (aFeature.EqualsLiteral("WEBRTC_HW_ACCELERATION_ENCODE"))
375 return nsIGfxInfo::FEATURE_WEBRTC_HW_ACCELERATION_ENCODE;
376 else if (aFeature.EqualsLiteral("WEBRTC_HW_ACCELERATION_DECODE"))
377 return nsIGfxInfo::FEATURE_WEBRTC_HW_ACCELERATION_DECODE;
378 else if (aFeature.EqualsLiteral("WEBRTC_HW_ACCELERATION_H264"))
379 return nsIGfxInfo::FEATURE_WEBRTC_HW_ACCELERATION_H264;
380 else if (aFeature.EqualsLiteral("CANVAS2D_ACCELERATION"))
381 return nsIGfxInfo::FEATURE_CANVAS2D_ACCELERATION;
382 else if (aFeature.EqualsLiteral("DX_INTEROP2"))
383 return nsIGfxInfo::FEATURE_DX_INTEROP2;
384 else if (aFeature.EqualsLiteral("GPU_PROCESS"))
385 return nsIGfxInfo::FEATURE_GPU_PROCESS;
386 else if (aFeature.EqualsLiteral("WEBGL2"))
387 return nsIGfxInfo::FEATURE_WEBGL2;
388 else if (aFeature.EqualsLiteral("ADVANCED_LAYERS"))
389 return nsIGfxInfo::FEATURE_ADVANCED_LAYERS;
390 else if (aFeature.EqualsLiteral("D3D11_KEYED_MUTEX"))
391 return nsIGfxInfo::FEATURE_D3D11_KEYED_MUTEX;
392 else if (aFeature.EqualsLiteral("WEBRENDER"))
393 return nsIGfxInfo::FEATURE_WEBRENDER;
394 else if (aFeature.EqualsLiteral("WEBRENDER_COMPOSITOR"))
395 return nsIGfxInfo::FEATURE_WEBRENDER_COMPOSITOR;
396 else if (aFeature.EqualsLiteral("DX_NV12"))
397 return nsIGfxInfo::FEATURE_DX_NV12;
398 // We do not support FEATURE_VP8_HW_DECODE and FEATURE_VP9_HW_DECODE
399 // in downloadable blocklist.
400 else if (aFeature.EqualsLiteral("GL_SWIZZLE"))
401 return nsIGfxInfo::FEATURE_GL_SWIZZLE;
402
403 // If we don't recognize the feature, it may be new, and something
404 // this version doesn't understand. So, nothing to do. This is
405 // different from feature not being specified at all, in which case
406 // this method should not get called and we should continue with the
407 // "all features" blocklisting.
408 return -1;
409 }
410
BlacklistFeatureStatusToGfxFeatureStatus(const nsAString & aStatus)411 static int32_t BlacklistFeatureStatusToGfxFeatureStatus(
412 const nsAString& aStatus) {
413 if (aStatus.EqualsLiteral("STATUS_OK"))
414 return nsIGfxInfo::FEATURE_STATUS_OK;
415 else if (aStatus.EqualsLiteral("BLOCKED_DRIVER_VERSION"))
416 return nsIGfxInfo::FEATURE_BLOCKED_DRIVER_VERSION;
417 else if (aStatus.EqualsLiteral("BLOCKED_DEVICE"))
418 return nsIGfxInfo::FEATURE_BLOCKED_DEVICE;
419 else if (aStatus.EqualsLiteral("DISCOURAGED"))
420 return nsIGfxInfo::FEATURE_DISCOURAGED;
421 else if (aStatus.EqualsLiteral("BLOCKED_OS_VERSION"))
422 return nsIGfxInfo::FEATURE_BLOCKED_OS_VERSION;
423 else if (aStatus.EqualsLiteral("DENIED"))
424 return nsIGfxInfo::FEATURE_DENIED;
425 else if (aStatus.EqualsLiteral("ALLOW_QUALIFIED"))
426 return nsIGfxInfo::FEATURE_ALLOW_QUALIFIED;
427 else if (aStatus.EqualsLiteral("ALLOW_ALWAYS"))
428 return nsIGfxInfo::FEATURE_ALLOW_ALWAYS;
429
430 // Do not allow it to set STATUS_UNKNOWN. Also, we are not
431 // expecting the "mismatch" status showing up here.
432
433 return nsIGfxInfo::FEATURE_STATUS_OK;
434 }
435
BlacklistComparatorToComparisonOp(const nsAString & op)436 static VersionComparisonOp BlacklistComparatorToComparisonOp(
437 const nsAString& op) {
438 if (op.EqualsLiteral("LESS_THAN"))
439 return DRIVER_LESS_THAN;
440 else if (op.EqualsLiteral("BUILD_ID_LESS_THAN"))
441 return DRIVER_BUILD_ID_LESS_THAN;
442 else if (op.EqualsLiteral("LESS_THAN_OR_EQUAL"))
443 return DRIVER_LESS_THAN_OR_EQUAL;
444 else if (op.EqualsLiteral("BUILD_ID_LESS_THAN_OR_EQUAL"))
445 return DRIVER_BUILD_ID_LESS_THAN_OR_EQUAL;
446 else if (op.EqualsLiteral("GREATER_THAN"))
447 return DRIVER_GREATER_THAN;
448 else if (op.EqualsLiteral("GREATER_THAN_OR_EQUAL"))
449 return DRIVER_GREATER_THAN_OR_EQUAL;
450 else if (op.EqualsLiteral("EQUAL"))
451 return DRIVER_EQUAL;
452 else if (op.EqualsLiteral("NOT_EQUAL"))
453 return DRIVER_NOT_EQUAL;
454 else if (op.EqualsLiteral("BETWEEN_EXCLUSIVE"))
455 return DRIVER_BETWEEN_EXCLUSIVE;
456 else if (op.EqualsLiteral("BETWEEN_INCLUSIVE"))
457 return DRIVER_BETWEEN_INCLUSIVE;
458 else if (op.EqualsLiteral("BETWEEN_INCLUSIVE_START"))
459 return DRIVER_BETWEEN_INCLUSIVE_START;
460
461 return DRIVER_COMPARISON_IGNORED;
462 }
463
464 /*
465 Deserialize Blacklist entries from string.
466 e.g:
467 os:WINNT 6.0\tvendor:0x8086\tdevices:0x2582,0x2782\tfeature:DIRECT3D_10_LAYERS\tfeatureStatus:BLOCKED_DRIVER_VERSION\tdriverVersion:8.52.322.2202\tdriverVersionComparator:LESS_THAN_OR_EQUAL
468 */
BlacklistEntryToDriverInfo(nsCString & aBlacklistEntry,GfxDriverInfo & aDriverInfo)469 static bool BlacklistEntryToDriverInfo(nsCString& aBlacklistEntry,
470 GfxDriverInfo& aDriverInfo) {
471 // If we get an application version to be zero, something is not working
472 // and we are not going to bother checking the blocklist versions.
473 // See TestGfxWidgets.cpp for how version comparison works.
474 // <versionRange minVersion="42.0a1" maxVersion="45.0"></versionRange>
475 static mozilla::Version zeroV("0");
476 static mozilla::Version appV(GfxInfoBase::GetApplicationVersion().get());
477 if (appV <= zeroV) {
478 gfxCriticalErrorOnce(gfxCriticalError::DefaultOptions(false))
479 << "Invalid application version "
480 << GfxInfoBase::GetApplicationVersion().get();
481 }
482
483 nsTArray<nsCString> keyValues;
484 ParseString(aBlacklistEntry, '\t', keyValues);
485
486 aDriverInfo.mRuleId =
487 NS_LITERAL_CSTRING("FEATURE_FAILURE_DL_BLACKLIST_NO_ID");
488
489 for (uint32_t i = 0; i < keyValues.Length(); ++i) {
490 nsCString keyValue = keyValues[i];
491 nsTArray<nsCString> splitted;
492 ParseString(keyValue, ':', splitted);
493 if (splitted.Length() != 2) {
494 // If we don't recognize the input data, we do not want to proceed.
495 gfxCriticalErrorOnce(CriticalLog::DefaultOptions(false))
496 << "Unrecognized data " << keyValue.get();
497 return false;
498 }
499 nsCString key = splitted[0];
500 nsCString value = splitted[1];
501 NS_ConvertUTF8toUTF16 dataValue(value);
502
503 if (value.Length() == 0) {
504 // Safety check for empty values.
505 gfxCriticalErrorOnce(CriticalLog::DefaultOptions(false))
506 << "Empty value for " << key.get();
507 return false;
508 }
509
510 if (key.EqualsLiteral("blockID")) {
511 nsCString blockIdStr =
512 NS_LITERAL_CSTRING("FEATURE_FAILURE_DL_BLACKLIST_") + value;
513 aDriverInfo.mRuleId = blockIdStr.get();
514 } else if (key.EqualsLiteral("os")) {
515 aDriverInfo.mOperatingSystem = BlacklistOSToOperatingSystem(dataValue);
516 } else if (key.EqualsLiteral("osversion")) {
517 aDriverInfo.mOperatingSystemVersion = strtoul(value.get(), nullptr, 10);
518 } else if (key.EqualsLiteral("desktopEnvironment")) {
519 aDriverInfo.mDesktopEnvironment = dataValue;
520 } else if (key.EqualsLiteral("windowProtocol")) {
521 aDriverInfo.mWindowProtocol = dataValue;
522 } else if (key.EqualsLiteral("vendor")) {
523 aDriverInfo.mAdapterVendor = dataValue;
524 } else if (key.EqualsLiteral("driverVendor")) {
525 aDriverInfo.mDriverVendor = dataValue;
526 } else if (key.EqualsLiteral("feature")) {
527 aDriverInfo.mFeature = BlacklistFeatureToGfxFeature(dataValue);
528 if (aDriverInfo.mFeature < 0) {
529 // If we don't recognize the feature, we do not want to proceed.
530 gfxCriticalErrorOnce(CriticalLog::DefaultOptions(false))
531 << "Unrecognized feature " << value.get();
532 return false;
533 }
534 } else if (key.EqualsLiteral("featureStatus")) {
535 aDriverInfo.mFeatureStatus =
536 BlacklistFeatureStatusToGfxFeatureStatus(dataValue);
537 } else if (key.EqualsLiteral("driverVersion")) {
538 uint64_t version;
539 if (ParseDriverVersion(dataValue, &version))
540 aDriverInfo.mDriverVersion = version;
541 } else if (key.EqualsLiteral("driverVersionMax")) {
542 uint64_t version;
543 if (ParseDriverVersion(dataValue, &version))
544 aDriverInfo.mDriverVersionMax = version;
545 } else if (key.EqualsLiteral("driverVersionComparator")) {
546 aDriverInfo.mComparisonOp = BlacklistComparatorToComparisonOp(dataValue);
547 } else if (key.EqualsLiteral("model")) {
548 aDriverInfo.mModel = dataValue;
549 } else if (key.EqualsLiteral("product")) {
550 aDriverInfo.mProduct = dataValue;
551 } else if (key.EqualsLiteral("manufacturer")) {
552 aDriverInfo.mManufacturer = dataValue;
553 } else if (key.EqualsLiteral("hardware")) {
554 aDriverInfo.mHardware = dataValue;
555 } else if (key.EqualsLiteral("versionRange")) {
556 nsTArray<nsCString> versionRange;
557 ParseString(value, ',', versionRange);
558 if (versionRange.Length() != 2) {
559 gfxCriticalErrorOnce(CriticalLog::DefaultOptions(false))
560 << "Unrecognized versionRange " << value.get();
561 return false;
562 }
563 nsCString minValue = versionRange[0];
564 nsCString maxValue = versionRange[1];
565
566 mozilla::Version minV(minValue.get());
567 mozilla::Version maxV(maxValue.get());
568
569 if (minV > zeroV && !(appV >= minV)) {
570 // The version of the application is less than the minimal version
571 // this blocklist entry applies to, so we can just ignore it by
572 // returning false and letting the caller deal with it.
573 return false;
574 }
575 if (maxV > zeroV && !(appV <= maxV)) {
576 // The version of the application is more than the maximal version
577 // this blocklist entry applies to, so we can just ignore it by
578 // returning false and letting the caller deal with it.
579 return false;
580 }
581 } else if (key.EqualsLiteral("devices")) {
582 nsTArray<nsCString> devices;
583 ParseString(value, ',', devices);
584 GfxDeviceFamily* deviceIds = BlacklistDevicesToDeviceFamily(devices);
585 if (deviceIds) {
586 // Get GfxDriverInfo to adopt the devices array we created.
587 aDriverInfo.mDeleteDevices = true;
588 aDriverInfo.mDevices = deviceIds;
589 }
590 }
591 // We explicitly ignore unknown elements.
592 }
593
594 return true;
595 }
596
BlacklistEntriesToDriverInfo(nsTArray<nsCString> & aBlacklistEntries,nsTArray<GfxDriverInfo> & aDriverInfo)597 static void BlacklistEntriesToDriverInfo(nsTArray<nsCString>& aBlacklistEntries,
598 nsTArray<GfxDriverInfo>& aDriverInfo) {
599 aDriverInfo.Clear();
600 aDriverInfo.SetLength(aBlacklistEntries.Length());
601
602 for (uint32_t i = 0; i < aBlacklistEntries.Length(); ++i) {
603 nsCString blacklistEntry = aBlacklistEntries[i];
604 GfxDriverInfo di;
605 if (BlacklistEntryToDriverInfo(blacklistEntry, di)) {
606 aDriverInfo[i] = di;
607 // Prevent di falling out of scope from destroying the devices.
608 di.mDeleteDevices = false;
609 }
610 }
611 }
612
613 NS_IMETHODIMP
Observe(nsISupports * aSubject,const char * aTopic,const char16_t * aData)614 GfxInfoBase::Observe(nsISupports* aSubject, const char* aTopic,
615 const char16_t* aData) {
616 if (strcmp(aTopic, "blocklist-data-gfxItems") == 0) {
617 nsTArray<GfxDriverInfo> driverInfo;
618 nsTArray<nsCString> blacklistEntries;
619 nsCString utf8Data = NS_ConvertUTF16toUTF8(aData);
620 if (utf8Data.Length() > 0) {
621 ParseString(utf8Data, '\n', blacklistEntries);
622 }
623 BlacklistEntriesToDriverInfo(blacklistEntries, driverInfo);
624 EvaluateDownloadedBlacklist(driverInfo);
625 }
626
627 return NS_OK;
628 }
629
GfxInfoBase()630 GfxInfoBase::GfxInfoBase() : mScreenPixels(INT64_MAX), mMutex("GfxInfoBase") {}
631
632 GfxInfoBase::~GfxInfoBase() = default;
633
Init()634 nsresult GfxInfoBase::Init() {
635 InitGfxDriverInfoShutdownObserver();
636
637 nsCOMPtr<nsIObserverService> os = mozilla::services::GetObserverService();
638 if (os) {
639 os->AddObserver(this, "blocklist-data-gfxItems", true);
640 }
641
642 return NS_OK;
643 }
644
GetData()645 void GfxInfoBase::GetData() {
646 if (mScreenPixels != INT64_MAX) {
647 // Already initialized.
648 return;
649 }
650
651 nsCOMPtr<nsIScreenManager> manager =
652 do_GetService("@mozilla.org/gfx/screenmanager;1");
653 if (!manager) {
654 MOZ_ASSERT_UNREACHABLE("failed to get nsIScreenManager");
655 return;
656 }
657
658 manager->GetTotalScreenPixels(&mScreenPixels);
659 }
660
661 NS_IMETHODIMP
GetFeatureStatus(int32_t aFeature,nsACString & aFailureId,int32_t * aStatus)662 GfxInfoBase::GetFeatureStatus(int32_t aFeature, nsACString& aFailureId,
663 int32_t* aStatus) {
664 // Ignore the gfx.blocklist.all pref on release and beta.
665 #if defined(RELEASE_OR_BETA)
666 int32_t blocklistAll = 0;
667 #else
668 int32_t blocklistAll = StaticPrefs::gfx_blocklist_all_AtStartup();
669 #endif
670 if (blocklistAll > 0) {
671 gfxCriticalErrorOnce(gfxCriticalError::DefaultOptions(false))
672 << "Forcing blocklisting all features";
673 *aStatus = FEATURE_BLOCKED_DEVICE;
674 aFailureId = "FEATURE_FAILURE_BLOCK_ALL";
675 return NS_OK;
676 } else if (blocklistAll < 0) {
677 gfxCriticalErrorOnce(gfxCriticalError::DefaultOptions(false))
678 << "Ignoring any feature blocklisting.";
679 *aStatus = FEATURE_STATUS_OK;
680 return NS_OK;
681 }
682
683 if (GetPrefValueForFeature(aFeature, *aStatus, aFailureId)) {
684 return NS_OK;
685 }
686
687 if (XRE_IsContentProcess()) {
688 // Use the cached data received from the parent process.
689 MOZ_ASSERT(sFeatureStatus);
690 bool success = false;
691 for (const auto& fs : *sFeatureStatus) {
692 if (fs.feature() == aFeature) {
693 aFailureId = fs.failureId();
694 *aStatus = fs.status();
695 success = true;
696 break;
697 }
698 }
699 return success ? NS_OK : NS_ERROR_FAILURE;
700 }
701
702 nsString version;
703 nsTArray<GfxDriverInfo> driverInfo;
704 nsresult rv =
705 GetFeatureStatusImpl(aFeature, aStatus, version, driverInfo, aFailureId);
706 return rv;
707 }
708
GetAllFeatures(dom::XPCOMInitData & xpcomInit)709 void GfxInfoBase::GetAllFeatures(dom::XPCOMInitData& xpcomInit) {
710 MOZ_RELEASE_ASSERT(XRE_IsParentProcess());
711 if (!sFeatureStatus) {
712 sFeatureStatus = new nsTArray<dom::GfxInfoFeatureStatus>();
713 for (int32_t i = 1; i <= nsIGfxInfo::FEATURE_MAX_VALUE; ++i) {
714 int32_t status = 0;
715 nsAutoCString failureId;
716 GetFeatureStatus(i, failureId, &status);
717 dom::GfxInfoFeatureStatus gfxFeatureStatus;
718 gfxFeatureStatus.feature() = i;
719 gfxFeatureStatus.status() = status;
720 gfxFeatureStatus.failureId() = failureId;
721 sFeatureStatus->AppendElement(gfxFeatureStatus);
722 }
723 }
724 for (const auto& status : *sFeatureStatus) {
725 dom::GfxInfoFeatureStatus copy = status;
726 xpcomInit.gfxFeatureStatus().AppendElement(copy);
727 }
728 }
729
MatchingAllowStatus(int32_t aStatus)730 inline bool MatchingAllowStatus(int32_t aStatus) {
731 switch (aStatus) {
732 case nsIGfxInfo::FEATURE_ALLOW_ALWAYS:
733 case nsIGfxInfo::FEATURE_ALLOW_QUALIFIED:
734 return true;
735 default:
736 return false;
737 }
738 }
739
740 // Matching OS go somewhat beyond the simple equality check because of the
741 // "All Windows" and "All OS X" variations.
742 //
743 // aBlockedOS is describing the system(s) we are trying to block.
744 // aSystemOS is describing the system we are running on.
745 //
746 // aSystemOS should not be "Windows" or "OSX" - it should be set to
747 // a particular version instead.
748 // However, it is valid for aBlockedOS to be one of those generic values,
749 // as we could be blocking all of the versions.
MatchingOperatingSystems(OperatingSystem aBlockedOS,OperatingSystem aSystemOS,uint32_t aSystemOSBuild)750 inline bool MatchingOperatingSystems(OperatingSystem aBlockedOS,
751 OperatingSystem aSystemOS,
752 uint32_t aSystemOSBuild) {
753 MOZ_ASSERT(aSystemOS != OperatingSystem::Windows &&
754 aSystemOS != OperatingSystem::OSX);
755
756 // If the block entry OS is unknown, it doesn't match
757 if (aBlockedOS == OperatingSystem::Unknown) {
758 return false;
759 }
760
761 #if defined(XP_WIN)
762 if (aBlockedOS == OperatingSystem::Windows) {
763 // We do want even "unknown" aSystemOS to fall under "all windows"
764 return true;
765 }
766
767 constexpr uint32_t kMinWin10BuildNumber = 18362;
768 if (aBlockedOS == OperatingSystem::RecentWindows10 &&
769 aSystemOS == OperatingSystem::Windows10) {
770 // For allowlist purposes, we sometimes want to restrict to only recent
771 // versions of Windows 10. This is a bit of a kludge but easier than adding
772 // complicated blocklist infrastructure for build ID comparisons like driver
773 // versions.
774 return aSystemOSBuild >= kMinWin10BuildNumber;
775 }
776
777 if (aBlockedOS == OperatingSystem::NotRecentWindows10) {
778 if (aSystemOS == OperatingSystem::Windows10) {
779 return aSystemOSBuild < kMinWin10BuildNumber;
780 } else {
781 return true;
782 }
783 }
784 #endif
785
786 #if defined(XP_MACOSX)
787 if (aBlockedOS == OperatingSystem::OSX) {
788 // We do want even "unknown" aSystemOS to fall under "all OS X"
789 return true;
790 }
791 #endif
792
793 return aSystemOS == aBlockedOS;
794 }
795
MatchingBattery(BatteryStatus aBatteryStatus,bool aHasBattery)796 inline bool MatchingBattery(BatteryStatus aBatteryStatus, bool aHasBattery) {
797 switch (aBatteryStatus) {
798 case BatteryStatus::All:
799 return true;
800 case BatteryStatus::None:
801 return !aHasBattery;
802 case BatteryStatus::Present:
803 return aHasBattery;
804 }
805
806 MOZ_ASSERT_UNREACHABLE("bad battery status");
807 return false;
808 }
809
MatchingScreenSize(ScreenSizeStatus aScreenStatus,int64_t aScreenPixels)810 inline bool MatchingScreenSize(ScreenSizeStatus aScreenStatus,
811 int64_t aScreenPixels) {
812 constexpr int64_t kMaxSmallPixels = 2304000; // 1920x1200
813 constexpr int64_t kMaxMediumPixels = 4953600; // 3440x1440
814
815 switch (aScreenStatus) {
816 case ScreenSizeStatus::All:
817 return true;
818 case ScreenSizeStatus::Small:
819 return aScreenPixels <= kMaxSmallPixels;
820 case ScreenSizeStatus::SmallAndMedium:
821 return aScreenPixels <= kMaxMediumPixels;
822 case ScreenSizeStatus::Medium:
823 return aScreenPixels > kMaxSmallPixels &&
824 aScreenPixels <= kMaxMediumPixels;
825 case ScreenSizeStatus::MediumAndLarge:
826 return aScreenPixels > kMaxSmallPixels;
827 case ScreenSizeStatus::Large:
828 return aScreenPixels > kMaxMediumPixels;
829 }
830
831 MOZ_ASSERT_UNREACHABLE("bad screen status");
832 return false;
833 }
834
FindBlocklistedDeviceInList(const nsTArray<GfxDriverInfo> & info,nsAString & aSuggestedVersion,int32_t aFeature,nsACString & aFailureId,OperatingSystem os,bool aForAllowing)835 int32_t GfxInfoBase::FindBlocklistedDeviceInList(
836 const nsTArray<GfxDriverInfo>& info, nsAString& aSuggestedVersion,
837 int32_t aFeature, nsACString& aFailureId, OperatingSystem os,
838 bool aForAllowing) {
839 int32_t status = nsIGfxInfo::FEATURE_STATUS_UNKNOWN;
840
841 // Some properties are not available on all platforms.
842 nsAutoString desktopEnvironment;
843 nsresult rv = GetDesktopEnvironment(desktopEnvironment);
844 if (NS_FAILED(rv) && rv != NS_ERROR_NOT_IMPLEMENTED) {
845 return 0;
846 }
847
848 nsAutoString windowProtocol;
849 rv = GetWindowProtocol(windowProtocol);
850 if (NS_FAILED(rv) && rv != NS_ERROR_NOT_IMPLEMENTED) {
851 return 0;
852 }
853
854 bool hasBattery = false;
855 rv = GetHasBattery(&hasBattery);
856 if (NS_FAILED(rv) && rv != NS_ERROR_NOT_IMPLEMENTED) {
857 return 0;
858 }
859
860 uint32_t osBuild = OperatingSystemBuild();
861
862 // Get the adapters once then reuse below
863 nsAutoString adapterVendorID[2];
864 nsAutoString adapterDeviceID[2];
865 nsAutoString adapterDriverVendor[2];
866 nsAutoString adapterDriverVersionString[2];
867 bool adapterInfoFailed[2];
868
869 adapterInfoFailed[0] =
870 (NS_FAILED(GetAdapterVendorID(adapterVendorID[0])) ||
871 NS_FAILED(GetAdapterDeviceID(adapterDeviceID[0])) ||
872 NS_FAILED(GetAdapterDriverVendor(adapterDriverVendor[0])) ||
873 NS_FAILED(GetAdapterDriverVersion(adapterDriverVersionString[0])));
874 adapterInfoFailed[1] =
875 (NS_FAILED(GetAdapterVendorID2(adapterVendorID[1])) ||
876 NS_FAILED(GetAdapterDeviceID2(adapterDeviceID[1])) ||
877 NS_FAILED(GetAdapterDriverVendor2(adapterDriverVendor[1])) ||
878 NS_FAILED(GetAdapterDriverVersion2(adapterDriverVersionString[1])));
879 // No point in going on if we don't have adapter info
880 if (adapterInfoFailed[0] && adapterInfoFailed[1]) {
881 return 0;
882 }
883
884 #if defined(XP_WIN) || defined(ANDROID) || defined(MOZ_X11)
885 uint64_t driverVersion[2] = {0, 0};
886 if (!adapterInfoFailed[0]) {
887 ParseDriverVersion(adapterDriverVersionString[0], &driverVersion[0]);
888 }
889 if (!adapterInfoFailed[1]) {
890 ParseDriverVersion(adapterDriverVersionString[1], &driverVersion[1]);
891 }
892 #endif
893
894 uint32_t i = 0;
895 for (; i < info.Length(); i++) {
896 // If the status is FEATURE_ALLOW_*, then it is for the allowlist, not
897 // blocklisting. Only consider entries for our search mode.
898 if (MatchingAllowStatus(info[i].mFeatureStatus) != aForAllowing) {
899 continue;
900 }
901
902 // If we don't have the info for this GPU, no need to check further.
903 // It is unclear that we would ever have a mixture of 1st and 2nd
904 // GPU, but leaving the code in for that possibility for now.
905 // (Actually, currently mGpu2 will never be true, so this can
906 // be optimized out.)
907 uint32_t infoIndex = info[i].mGpu2 ? 1 : 0;
908 if (adapterInfoFailed[infoIndex]) {
909 continue;
910 }
911
912 // Do the operating system check first, no point in getting the driver
913 // info if we won't need to use it.
914 if (!MatchingOperatingSystems(info[i].mOperatingSystem, os, osBuild)) {
915 continue;
916 }
917
918 if (info[i].mOperatingSystemVersion &&
919 info[i].mOperatingSystemVersion != OperatingSystemVersion()) {
920 continue;
921 }
922
923 if (!MatchingBattery(info[i].mBattery, hasBattery)) {
924 continue;
925 }
926
927 if (!MatchingScreenSize(info[i].mScreen, mScreenPixels)) {
928 continue;
929 }
930
931 if (!DoesDesktopEnvironmentMatch(info[i].mDesktopEnvironment,
932 desktopEnvironment)) {
933 continue;
934 }
935
936 if (!DoesWindowProtocolMatch(info[i].mWindowProtocol, windowProtocol)) {
937 continue;
938 }
939
940 if (!DoesVendorMatch(info[i].mAdapterVendor, adapterVendorID[infoIndex])) {
941 continue;
942 }
943
944 if (!DoesDriverVendorMatch(info[i].mDriverVendor,
945 adapterDriverVendor[infoIndex])) {
946 continue;
947 }
948
949 if (info[i].mDevices && !info[i].mDevices->IsEmpty()) {
950 nsresult rv = info[i].mDevices->Contains(adapterDeviceID[infoIndex]);
951 if (rv == NS_ERROR_NOT_AVAILABLE) {
952 // Not found
953 continue;
954 }
955 if (rv != NS_OK) {
956 // Failed to search, allowlist should not match, blocklist should match
957 // for safety reasons
958 if (aForAllowing) {
959 continue;
960 }
961 break;
962 }
963 }
964
965 bool match = false;
966
967 if (!info[i].mHardware.IsEmpty() && !info[i].mHardware.Equals(Hardware())) {
968 continue;
969 }
970 if (!info[i].mModel.IsEmpty() && !info[i].mModel.Equals(Model())) {
971 continue;
972 }
973 if (!info[i].mProduct.IsEmpty() && !info[i].mProduct.Equals(Product())) {
974 continue;
975 }
976 if (!info[i].mManufacturer.IsEmpty() &&
977 !info[i].mManufacturer.Equals(Manufacturer())) {
978 continue;
979 }
980
981 #if defined(XP_WIN) || defined(ANDROID) || defined(MOZ_X11)
982 switch (info[i].mComparisonOp) {
983 case DRIVER_LESS_THAN:
984 match = driverVersion[infoIndex] < info[i].mDriverVersion;
985 break;
986 case DRIVER_BUILD_ID_LESS_THAN:
987 match = (driverVersion[infoIndex] & 0xFFFF) < info[i].mDriverVersion;
988 break;
989 case DRIVER_LESS_THAN_OR_EQUAL:
990 match = driverVersion[infoIndex] <= info[i].mDriverVersion;
991 break;
992 case DRIVER_BUILD_ID_LESS_THAN_OR_EQUAL:
993 match = (driverVersion[infoIndex] & 0xFFFF) <= info[i].mDriverVersion;
994 break;
995 case DRIVER_GREATER_THAN:
996 match = driverVersion[infoIndex] > info[i].mDriverVersion;
997 break;
998 case DRIVER_GREATER_THAN_OR_EQUAL:
999 match = driverVersion[infoIndex] >= info[i].mDriverVersion;
1000 break;
1001 case DRIVER_EQUAL:
1002 match = driverVersion[infoIndex] == info[i].mDriverVersion;
1003 break;
1004 case DRIVER_NOT_EQUAL:
1005 match = driverVersion[infoIndex] != info[i].mDriverVersion;
1006 break;
1007 case DRIVER_BETWEEN_EXCLUSIVE:
1008 match = driverVersion[infoIndex] > info[i].mDriverVersion &&
1009 driverVersion[infoIndex] < info[i].mDriverVersionMax;
1010 break;
1011 case DRIVER_BETWEEN_INCLUSIVE:
1012 match = driverVersion[infoIndex] >= info[i].mDriverVersion &&
1013 driverVersion[infoIndex] <= info[i].mDriverVersionMax;
1014 break;
1015 case DRIVER_BETWEEN_INCLUSIVE_START:
1016 match = driverVersion[infoIndex] >= info[i].mDriverVersion &&
1017 driverVersion[infoIndex] < info[i].mDriverVersionMax;
1018 break;
1019 case DRIVER_COMPARISON_IGNORED:
1020 // We don't have a comparison op, so we match everything.
1021 match = true;
1022 break;
1023 default:
1024 NS_WARNING("Bogus op in GfxDriverInfo");
1025 break;
1026 }
1027 #else
1028 // We don't care what driver version it was. We only check OS version and if
1029 // the device matches.
1030 match = true;
1031 #endif
1032
1033 if (match || info[i].mDriverVersion == GfxDriverInfo::allDriverVersions) {
1034 if (info[i].mFeature == GfxDriverInfo::allFeatures ||
1035 info[i].mFeature == aFeature) {
1036 status = info[i].mFeatureStatus;
1037 if (!info[i].mRuleId.IsEmpty()) {
1038 aFailureId = info[i].mRuleId.get();
1039 } else {
1040 aFailureId = "FEATURE_FAILURE_DL_BLACKLIST_NO_ID";
1041 }
1042 break;
1043 }
1044 }
1045 }
1046
1047 #if defined(XP_WIN)
1048 // As a very special case, we block D2D on machines with an NVidia 310M GPU
1049 // as either the primary or secondary adapter. D2D is also blocked when the
1050 // NV 310M is the primary adapter (using the standard blocklisting mechanism).
1051 // If the primary GPU already matched something in the blocklist then we
1052 // ignore this special rule. See bug 1008759.
1053 if (status == nsIGfxInfo::FEATURE_STATUS_UNKNOWN &&
1054 (aFeature == nsIGfxInfo::FEATURE_DIRECT2D)) {
1055 if (!adapterInfoFailed[1]) {
1056 nsAString& nvVendorID =
1057 (nsAString&)GfxDriverInfo::GetDeviceVendor(DeviceVendor::NVIDIA);
1058 const nsString nv310mDeviceId = NS_LITERAL_STRING("0x0A70");
1059 if (nvVendorID.Equals(adapterVendorID[1],
1060 nsCaseInsensitiveStringComparator) &&
1061 nv310mDeviceId.Equals(adapterDeviceID[1],
1062 nsCaseInsensitiveStringComparator)) {
1063 status = nsIGfxInfo::FEATURE_BLOCKED_DEVICE;
1064 aFailureId = "FEATURE_FAILURE_D2D_NV310M_BLOCK";
1065 }
1066 }
1067 }
1068
1069 // Depends on Windows driver versioning. We don't pass a GfxDriverInfo object
1070 // back to the Windows handler, so we must handle this here.
1071 if (status == FEATURE_BLOCKED_DRIVER_VERSION) {
1072 if (info[i].mSuggestedVersion) {
1073 aSuggestedVersion.AppendPrintf("%s", info[i].mSuggestedVersion);
1074 } else if (info[i].mComparisonOp == DRIVER_LESS_THAN &&
1075 info[i].mDriverVersion != GfxDriverInfo::allDriverVersions) {
1076 aSuggestedVersion.AppendPrintf(
1077 "%lld.%lld.%lld.%lld",
1078 (info[i].mDriverVersion & 0xffff000000000000) >> 48,
1079 (info[i].mDriverVersion & 0x0000ffff00000000) >> 32,
1080 (info[i].mDriverVersion & 0x00000000ffff0000) >> 16,
1081 (info[i].mDriverVersion & 0x000000000000ffff));
1082 }
1083 }
1084 #endif
1085
1086 return status;
1087 }
1088
SetFeatureStatus(const nsTArray<dom::GfxInfoFeatureStatus> & aFS)1089 void GfxInfoBase::SetFeatureStatus(
1090 const nsTArray<dom::GfxInfoFeatureStatus>& aFS) {
1091 MOZ_ASSERT(!sFeatureStatus);
1092 sFeatureStatus = new nsTArray<dom::GfxInfoFeatureStatus>(aFS.Clone());
1093 }
1094
DoesDesktopEnvironmentMatch(const nsAString & aBlocklistDesktop,const nsAString & aDesktopEnv)1095 bool GfxInfoBase::DoesDesktopEnvironmentMatch(
1096 const nsAString& aBlocklistDesktop, const nsAString& aDesktopEnv) {
1097 return aBlocklistDesktop.Equals(aDesktopEnv,
1098 nsCaseInsensitiveStringComparator) ||
1099 aBlocklistDesktop.Equals(
1100 GfxDriverInfo::GetDesktopEnvironment(DesktopEnvironment::All),
1101 nsCaseInsensitiveStringComparator);
1102 }
1103
DoesWindowProtocolMatch(const nsAString & aBlocklistWindowProtocol,const nsAString & aWindowProtocol)1104 bool GfxInfoBase::DoesWindowProtocolMatch(
1105 const nsAString& aBlocklistWindowProtocol,
1106 const nsAString& aWindowProtocol) {
1107 return aBlocklistWindowProtocol.Equals(aWindowProtocol,
1108 nsCaseInsensitiveStringComparator) ||
1109 aBlocklistWindowProtocol.Equals(
1110 GfxDriverInfo::GetWindowProtocol(WindowProtocol::All),
1111 nsCaseInsensitiveStringComparator);
1112 }
1113
DoesVendorMatch(const nsAString & aBlocklistVendor,const nsAString & aAdapterVendor)1114 bool GfxInfoBase::DoesVendorMatch(const nsAString& aBlocklistVendor,
1115 const nsAString& aAdapterVendor) {
1116 return aBlocklistVendor.Equals(aAdapterVendor,
1117 nsCaseInsensitiveStringComparator) ||
1118 aBlocklistVendor.Equals(
1119 GfxDriverInfo::GetDeviceVendor(DeviceVendor::All),
1120 nsCaseInsensitiveStringComparator);
1121 }
1122
DoesDriverVendorMatch(const nsAString & aBlocklistVendor,const nsAString & aDriverVendor)1123 bool GfxInfoBase::DoesDriverVendorMatch(const nsAString& aBlocklistVendor,
1124 const nsAString& aDriverVendor) {
1125 return aBlocklistVendor.Equals(aDriverVendor,
1126 nsCaseInsensitiveStringComparator) ||
1127 aBlocklistVendor.Equals(
1128 GfxDriverInfo::GetDriverVendor(DriverVendor::All),
1129 nsCaseInsensitiveStringComparator);
1130 }
1131
IsFeatureAllowlisted(int32_t aFeature) const1132 bool GfxInfoBase::IsFeatureAllowlisted(int32_t aFeature) const {
1133 return aFeature == nsIGfxInfo::FEATURE_WEBRENDER;
1134 }
1135
GetFeatureStatusImpl(int32_t aFeature,int32_t * aStatus,nsAString & aSuggestedVersion,const nsTArray<GfxDriverInfo> & aDriverInfo,nsACString & aFailureId,OperatingSystem * aOS)1136 nsresult GfxInfoBase::GetFeatureStatusImpl(
1137 int32_t aFeature, int32_t* aStatus, nsAString& aSuggestedVersion,
1138 const nsTArray<GfxDriverInfo>& aDriverInfo, nsACString& aFailureId,
1139 OperatingSystem* aOS /* = nullptr */) {
1140 if (aFeature <= 0) {
1141 gfxWarning() << "Invalid feature <= 0";
1142 return NS_OK;
1143 }
1144
1145 if (*aStatus != nsIGfxInfo::FEATURE_STATUS_UNKNOWN) {
1146 // Terminate now with the status determined by the derived type (OS-specific
1147 // code).
1148 return NS_OK;
1149 }
1150
1151 if (sShutdownOccurred) {
1152 // This is futile; we've already commenced shutdown and our blocklists have
1153 // been deleted. We may want to look into resurrecting the blocklist instead
1154 // but for now, just don't even go there.
1155 return NS_OK;
1156 }
1157
1158 // Ensure any additional initialization required is complete.
1159 GetData();
1160
1161 // If an operating system was provided by the derived GetFeatureStatusImpl,
1162 // grab it here. Otherwise, the OS is unknown.
1163 OperatingSystem os = (aOS ? *aOS : OperatingSystem::Unknown);
1164
1165 nsAutoString adapterVendorID;
1166 nsAutoString adapterDeviceID;
1167 nsAutoString adapterDriverVersionString;
1168 if (NS_FAILED(GetAdapterVendorID(adapterVendorID)) ||
1169 NS_FAILED(GetAdapterDeviceID(adapterDeviceID)) ||
1170 NS_FAILED(GetAdapterDriverVersion(adapterDriverVersionString))) {
1171 aFailureId = "FEATURE_FAILURE_CANT_RESOLVE_ADAPTER";
1172 *aStatus = FEATURE_BLOCKED_DEVICE;
1173 return NS_OK;
1174 }
1175
1176 // Check if the device is blocked from the downloaded blocklist. If not, check
1177 // the static list after that. This order is used so that we can later escape
1178 // out of static blocks (i.e. if we were wrong or something was patched, we
1179 // can back out our static block without doing a release).
1180 int32_t status;
1181 if (aDriverInfo.Length()) {
1182 status =
1183 FindBlocklistedDeviceInList(aDriverInfo, aSuggestedVersion, aFeature,
1184 aFailureId, os, /* aForAllowing */ false);
1185 } else {
1186 if (!sDriverInfo) {
1187 sDriverInfo = new nsTArray<GfxDriverInfo>();
1188 }
1189 status = FindBlocklistedDeviceInList(GetGfxDriverInfo(), aSuggestedVersion,
1190 aFeature, aFailureId, os,
1191 /* aForAllowing */ false);
1192 }
1193
1194 if (status == nsIGfxInfo::FEATURE_STATUS_UNKNOWN) {
1195 if (IsFeatureAllowlisted(aFeature)) {
1196 // This feature is actually using the allowlist; that means after we pass
1197 // the blocklist to prevent us explicitly from getting the feature, we now
1198 // need to check the allowlist to ensure we are allowed to get it in the
1199 // first place.
1200 if (aDriverInfo.Length()) {
1201 status = FindBlocklistedDeviceInList(aDriverInfo, aSuggestedVersion,
1202 aFeature, aFailureId, os,
1203 /* aForAllowing */ true);
1204 } else {
1205 status = FindBlocklistedDeviceInList(
1206 GetGfxDriverInfo(), aSuggestedVersion, aFeature, aFailureId, os,
1207 /* aForAllowing */ true);
1208 }
1209
1210 if (status == nsIGfxInfo::FEATURE_STATUS_UNKNOWN) {
1211 status = nsIGfxInfo::FEATURE_DENIED;
1212 }
1213 } else {
1214 // It's now done being processed. It's safe to set the status to
1215 // STATUS_OK.
1216 status = nsIGfxInfo::FEATURE_STATUS_OK;
1217 }
1218 }
1219
1220 *aStatus = status;
1221 return NS_OK;
1222 }
1223
1224 NS_IMETHODIMP
GetFeatureSuggestedDriverVersion(int32_t aFeature,nsAString & aVersion)1225 GfxInfoBase::GetFeatureSuggestedDriverVersion(int32_t aFeature,
1226 nsAString& aVersion) {
1227 nsCString version;
1228 if (GetPrefValueForDriverVersion(version)) {
1229 aVersion = NS_ConvertASCIItoUTF16(version);
1230 return NS_OK;
1231 }
1232
1233 int32_t status;
1234 nsCString discardFailureId;
1235 nsTArray<GfxDriverInfo> driverInfo;
1236 return GetFeatureStatusImpl(aFeature, &status, aVersion, driverInfo,
1237 discardFailureId);
1238 }
1239
EvaluateDownloadedBlacklist(nsTArray<GfxDriverInfo> & aDriverInfo)1240 void GfxInfoBase::EvaluateDownloadedBlacklist(
1241 nsTArray<GfxDriverInfo>& aDriverInfo) {
1242 int32_t features[] = {nsIGfxInfo::FEATURE_DIRECT2D,
1243 nsIGfxInfo::FEATURE_DIRECT3D_9_LAYERS,
1244 nsIGfxInfo::FEATURE_DIRECT3D_10_LAYERS,
1245 nsIGfxInfo::FEATURE_DIRECT3D_10_1_LAYERS,
1246 nsIGfxInfo::FEATURE_DIRECT3D_11_LAYERS,
1247 nsIGfxInfo::FEATURE_DIRECT3D_11_ANGLE,
1248 nsIGfxInfo::FEATURE_HARDWARE_VIDEO_DECODING,
1249 nsIGfxInfo::FEATURE_OPENGL_LAYERS,
1250 nsIGfxInfo::FEATURE_WEBGL_OPENGL,
1251 nsIGfxInfo::FEATURE_WEBGL_ANGLE,
1252 nsIGfxInfo::FEATURE_WEBRTC_HW_ACCELERATION_ENCODE,
1253 nsIGfxInfo::FEATURE_WEBRTC_HW_ACCELERATION_DECODE,
1254 nsIGfxInfo::FEATURE_WEBGL_MSAA,
1255 nsIGfxInfo::FEATURE_STAGEFRIGHT,
1256 nsIGfxInfo::FEATURE_WEBRTC_HW_ACCELERATION_H264,
1257 nsIGfxInfo::FEATURE_CANVAS2D_ACCELERATION,
1258 nsIGfxInfo::FEATURE_VP8_HW_DECODE,
1259 nsIGfxInfo::FEATURE_VP9_HW_DECODE,
1260 nsIGfxInfo::FEATURE_DX_INTEROP2,
1261 nsIGfxInfo::FEATURE_GPU_PROCESS,
1262 nsIGfxInfo::FEATURE_WEBGL2,
1263 nsIGfxInfo::FEATURE_ADVANCED_LAYERS,
1264 nsIGfxInfo::FEATURE_D3D11_KEYED_MUTEX,
1265 nsIGfxInfo::FEATURE_WEBRENDER,
1266 nsIGfxInfo::FEATURE_WEBRENDER_COMPOSITOR,
1267 nsIGfxInfo::FEATURE_DX_NV12,
1268 nsIGfxInfo::FEATURE_DX_P010,
1269 nsIGfxInfo::FEATURE_DX_P016,
1270 nsIGfxInfo::FEATURE_GL_SWIZZLE,
1271 0};
1272
1273 // For every feature we know about, we evaluate whether this blacklist has a
1274 // non-STATUS_OK status. If it does, we set the pref we evaluate in
1275 // GetFeatureStatus above, so we don't need to hold on to this blacklist
1276 // anywhere permanent.
1277 int i = 0;
1278 while (features[i]) {
1279 int32_t status;
1280 nsCString failureId;
1281 nsAutoString suggestedVersion;
1282 if (NS_SUCCEEDED(GetFeatureStatusImpl(
1283 features[i], &status, suggestedVersion, aDriverInfo, failureId))) {
1284 switch (status) {
1285 default:
1286 case nsIGfxInfo::FEATURE_STATUS_OK:
1287 RemovePrefForFeature(features[i]);
1288 break;
1289
1290 case nsIGfxInfo::FEATURE_BLOCKED_DRIVER_VERSION:
1291 if (!suggestedVersion.IsEmpty()) {
1292 SetPrefValueForDriverVersion(suggestedVersion);
1293 } else {
1294 RemovePrefForDriverVersion();
1295 }
1296 [[fallthrough]];
1297
1298 case nsIGfxInfo::FEATURE_BLOCKED_MISMATCHED_VERSION:
1299 case nsIGfxInfo::FEATURE_BLOCKED_DEVICE:
1300 case nsIGfxInfo::FEATURE_DISCOURAGED:
1301 case nsIGfxInfo::FEATURE_BLOCKED_OS_VERSION:
1302 SetPrefValueForFeature(features[i], status, failureId);
1303 break;
1304 }
1305 }
1306
1307 ++i;
1308 }
1309 }
1310
NS_IMETHODIMP_(void)1311 NS_IMETHODIMP_(void)
1312 GfxInfoBase::LogFailure(const nsACString& failure) {
1313 // gfxCriticalError has a mutex lock of its own, so we may not actually
1314 // need this lock. ::GetFailures() accesses the data but the LogForwarder
1315 // will not return the copy of the logs unless it can get the same lock
1316 // that gfxCriticalError uses. Still, that is so much of an implementation
1317 // detail that it's nicer to just add an extra lock here and in
1318 // ::GetFailures()
1319 MutexAutoLock lock(mMutex);
1320
1321 // By default, gfxCriticalError asserts; make it not assert in this case.
1322 gfxCriticalError(CriticalLog::DefaultOptions(false))
1323 << "(LF) " << failure.BeginReading();
1324 }
1325
GetFailures(nsTArray<int32_t> & indices,nsTArray<nsCString> & failures)1326 NS_IMETHODIMP GfxInfoBase::GetFailures(nsTArray<int32_t>& indices,
1327 nsTArray<nsCString>& failures) {
1328 MutexAutoLock lock(mMutex);
1329
1330 LogForwarder* logForwarder = Factory::GetLogForwarder();
1331 if (!logForwarder) {
1332 return NS_ERROR_UNEXPECTED;
1333 }
1334
1335 // There are two string copies in this method, starting with this one. We are
1336 // assuming this is not a big deal, as the size of the array should be small
1337 // and the strings in it should be small as well (the error messages in the
1338 // code.) The second copy happens with the AppendElement() calls.
1339 // Technically, we don't need the mutex lock after the StringVectorCopy()
1340 // call.
1341 LoggingRecord loggedStrings = logForwarder->LoggingRecordCopy();
1342 LoggingRecord::const_iterator it;
1343 for (it = loggedStrings.begin(); it != loggedStrings.end(); ++it) {
1344 failures.AppendElement(
1345 nsDependentCSubstring(Get<1>(*it).c_str(), Get<1>(*it).size()));
1346 indices.AppendElement(Get<0>(*it));
1347 }
1348
1349 return NS_OK;
1350 }
1351
1352 nsTArray<GfxInfoCollectorBase*>* sCollectors;
1353
InitCollectors()1354 static void InitCollectors() {
1355 if (!sCollectors) sCollectors = new nsTArray<GfxInfoCollectorBase*>;
1356 }
1357
GetInfo(JSContext * aCx,JS::MutableHandle<JS::Value> aResult)1358 nsresult GfxInfoBase::GetInfo(JSContext* aCx,
1359 JS::MutableHandle<JS::Value> aResult) {
1360 InitCollectors();
1361 InfoObject obj(aCx);
1362
1363 for (uint32_t i = 0; i < sCollectors->Length(); i++) {
1364 (*sCollectors)[i]->GetInfo(obj);
1365 }
1366
1367 // Some example property definitions
1368 // obj.DefineProperty("wordCacheSize", gfxTextRunWordCache::Count());
1369 // obj.DefineProperty("renderer", mRendererIDsString);
1370 // obj.DefineProperty("five", 5);
1371
1372 if (!obj.mOk) {
1373 return NS_ERROR_FAILURE;
1374 }
1375
1376 aResult.setObject(*obj.mObj);
1377 return NS_OK;
1378 }
1379
1380 nsAutoCString gBaseAppVersion;
1381
GetApplicationVersion()1382 const nsCString& GfxInfoBase::GetApplicationVersion() {
1383 static bool versionInitialized = false;
1384 if (!versionInitialized) {
1385 // If we fail to get the version, we will not try again.
1386 versionInitialized = true;
1387
1388 // Get the version from xpcom/system/nsIXULAppInfo.idl
1389 nsCOMPtr<nsIXULAppInfo> app = do_GetService("@mozilla.org/xre/app-info;1");
1390 if (app) {
1391 app->GetVersion(gBaseAppVersion);
1392 }
1393 }
1394 return gBaseAppVersion;
1395 }
1396
AddCollector(GfxInfoCollectorBase * collector)1397 void GfxInfoBase::AddCollector(GfxInfoCollectorBase* collector) {
1398 InitCollectors();
1399 sCollectors->AppendElement(collector);
1400 }
1401
RemoveCollector(GfxInfoCollectorBase * collector)1402 void GfxInfoBase::RemoveCollector(GfxInfoCollectorBase* collector) {
1403 InitCollectors();
1404 for (uint32_t i = 0; i < sCollectors->Length(); i++) {
1405 if ((*sCollectors)[i] == collector) {
1406 sCollectors->RemoveElementAt(i);
1407 break;
1408 }
1409 }
1410 if (sCollectors->IsEmpty()) {
1411 delete sCollectors;
1412 sCollectors = nullptr;
1413 }
1414 }
1415
FindMonitors(JSContext * aCx,JS::HandleObject aOutArray)1416 nsresult GfxInfoBase::FindMonitors(JSContext* aCx, JS::HandleObject aOutArray) {
1417 // If we have no platform specific implementation for detecting monitors, we
1418 // can just get the screen size from gfxPlatform as the best guess.
1419 if (!gfxPlatform::Initialized()) {
1420 return NS_OK;
1421 }
1422
1423 // If the screen size is empty, we are probably in xpcshell.
1424 gfx::IntSize screenSize = gfxPlatform::GetPlatform()->GetScreenSize();
1425
1426 JS::Rooted<JSObject*> obj(aCx, JS_NewPlainObject(aCx));
1427
1428 JS::Rooted<JS::Value> screenWidth(aCx, JS::Int32Value(screenSize.width));
1429 JS_SetProperty(aCx, obj, "screenWidth", screenWidth);
1430
1431 JS::Rooted<JS::Value> screenHeight(aCx, JS::Int32Value(screenSize.height));
1432 JS_SetProperty(aCx, obj, "screenHeight", screenHeight);
1433
1434 JS::Rooted<JS::Value> element(aCx, JS::ObjectValue(*obj));
1435 JS_SetElement(aCx, aOutArray, 0, element);
1436
1437 return NS_OK;
1438 }
1439
1440 NS_IMETHODIMP
GetMonitors(JSContext * aCx,JS::MutableHandleValue aResult)1441 GfxInfoBase::GetMonitors(JSContext* aCx, JS::MutableHandleValue aResult) {
1442 JS::Rooted<JSObject*> array(aCx, JS::NewArrayObject(aCx, 0));
1443
1444 nsresult rv = FindMonitors(aCx, array);
1445 if (NS_FAILED(rv)) {
1446 return rv;
1447 }
1448
1449 aResult.setObject(*array);
1450 return NS_OK;
1451 }
1452
SetJSPropertyString(JSContext * aCx,JS::Handle<JSObject * > aObj,const char * aProp,const char * aString)1453 static inline bool SetJSPropertyString(JSContext* aCx,
1454 JS::Handle<JSObject*> aObj,
1455 const char* aProp, const char* aString) {
1456 JS::Rooted<JSString*> str(aCx, JS_NewStringCopyZ(aCx, aString));
1457 if (!str) {
1458 return false;
1459 }
1460
1461 JS::Rooted<JS::Value> val(aCx, JS::StringValue(str));
1462 return JS_SetProperty(aCx, aObj, aProp, val);
1463 }
1464
1465 template <typename T>
AppendJSElement(JSContext * aCx,JS::Handle<JSObject * > aObj,const T & aValue)1466 static inline bool AppendJSElement(JSContext* aCx, JS::Handle<JSObject*> aObj,
1467 const T& aValue) {
1468 uint32_t index;
1469 if (!JS::GetArrayLength(aCx, aObj, &index)) {
1470 return false;
1471 }
1472 return JS_SetElement(aCx, aObj, index, aValue);
1473 }
1474
GetFeatures(JSContext * aCx,JS::MutableHandle<JS::Value> aOut)1475 nsresult GfxInfoBase::GetFeatures(JSContext* aCx,
1476 JS::MutableHandle<JS::Value> aOut) {
1477 JS::Rooted<JSObject*> obj(aCx, JS_NewPlainObject(aCx));
1478 if (!obj) {
1479 return NS_ERROR_OUT_OF_MEMORY;
1480 }
1481 aOut.setObject(*obj);
1482
1483 layers::LayersBackend backend =
1484 gfxPlatform::Initialized()
1485 ? gfxPlatform::GetPlatform()->GetCompositorBackend()
1486 : layers::LayersBackend::LAYERS_NONE;
1487 const char* backendName = layers::GetLayersBackendName(backend);
1488 SetJSPropertyString(aCx, obj, "compositor", backendName);
1489
1490 // If graphics isn't initialized yet, just stop now.
1491 if (!gfxPlatform::Initialized()) {
1492 return NS_OK;
1493 }
1494
1495 DescribeFeatures(aCx, obj);
1496 return NS_OK;
1497 }
1498
GetFeatureLog(JSContext * aCx,JS::MutableHandle<JS::Value> aOut)1499 nsresult GfxInfoBase::GetFeatureLog(JSContext* aCx,
1500 JS::MutableHandle<JS::Value> aOut) {
1501 JS::Rooted<JSObject*> containerObj(aCx, JS_NewPlainObject(aCx));
1502 if (!containerObj) {
1503 return NS_ERROR_OUT_OF_MEMORY;
1504 }
1505 aOut.setObject(*containerObj);
1506
1507 JS::Rooted<JSObject*> featureArray(aCx, JS::NewArrayObject(aCx, 0));
1508 if (!featureArray) {
1509 return NS_ERROR_OUT_OF_MEMORY;
1510 }
1511
1512 // Collect features.
1513 gfxConfig::ForEachFeature([&](const char* aName, const char* aDescription,
1514 FeatureState& aFeature) -> void {
1515 JS::Rooted<JSObject*> obj(aCx, JS_NewPlainObject(aCx));
1516 if (!obj) {
1517 return;
1518 }
1519 if (!SetJSPropertyString(aCx, obj, "name", aName) ||
1520 !SetJSPropertyString(aCx, obj, "description", aDescription) ||
1521 !SetJSPropertyString(aCx, obj, "status",
1522 FeatureStatusToString(aFeature.GetValue()))) {
1523 return;
1524 }
1525
1526 JS::Rooted<JS::Value> log(aCx);
1527 if (!BuildFeatureStateLog(aCx, aFeature, &log)) {
1528 return;
1529 }
1530 if (!JS_SetProperty(aCx, obj, "log", log)) {
1531 return;
1532 }
1533
1534 if (!AppendJSElement(aCx, featureArray, obj)) {
1535 return;
1536 }
1537 });
1538
1539 JS::Rooted<JSObject*> fallbackArray(aCx, JS::NewArrayObject(aCx, 0));
1540 if (!fallbackArray) {
1541 return NS_ERROR_OUT_OF_MEMORY;
1542 }
1543
1544 // Collect fallbacks.
1545 gfxConfig::ForEachFallback(
1546 [&](const char* aName, const char* aMessage) -> void {
1547 JS::Rooted<JSObject*> obj(aCx, JS_NewPlainObject(aCx));
1548 if (!obj) {
1549 return;
1550 }
1551
1552 if (!SetJSPropertyString(aCx, obj, "name", aName) ||
1553 !SetJSPropertyString(aCx, obj, "message", aMessage)) {
1554 return;
1555 }
1556
1557 if (!AppendJSElement(aCx, fallbackArray, obj)) {
1558 return;
1559 }
1560 });
1561
1562 JS::Rooted<JS::Value> val(aCx);
1563
1564 val = JS::ObjectValue(*featureArray);
1565 JS_SetProperty(aCx, containerObj, "features", val);
1566
1567 val = JS::ObjectValue(*fallbackArray);
1568 JS_SetProperty(aCx, containerObj, "fallbacks", val);
1569
1570 return NS_OK;
1571 }
1572
BuildFeatureStateLog(JSContext * aCx,const FeatureState & aFeature,JS::MutableHandle<JS::Value> aOut)1573 bool GfxInfoBase::BuildFeatureStateLog(JSContext* aCx,
1574 const FeatureState& aFeature,
1575 JS::MutableHandle<JS::Value> aOut) {
1576 JS::Rooted<JSObject*> log(aCx, JS::NewArrayObject(aCx, 0));
1577 if (!log) {
1578 return false;
1579 }
1580 aOut.setObject(*log);
1581
1582 aFeature.ForEachStatusChange([&](const char* aType, FeatureStatus aStatus,
1583 const char* aMessage) -> void {
1584 JS::Rooted<JSObject*> obj(aCx, JS_NewPlainObject(aCx));
1585 if (!obj) {
1586 return;
1587 }
1588
1589 if (!SetJSPropertyString(aCx, obj, "type", aType) ||
1590 !SetJSPropertyString(aCx, obj, "status",
1591 FeatureStatusToString(aStatus)) ||
1592 (aMessage && !SetJSPropertyString(aCx, obj, "message", aMessage))) {
1593 return;
1594 }
1595
1596 if (!AppendJSElement(aCx, log, obj)) {
1597 return;
1598 }
1599 });
1600
1601 return true;
1602 }
1603
DescribeFeatures(JSContext * aCx,JS::Handle<JSObject * > aObj)1604 void GfxInfoBase::DescribeFeatures(JSContext* aCx, JS::Handle<JSObject*> aObj) {
1605 JS::Rooted<JSObject*> obj(aCx);
1606
1607 gfx::FeatureState& hwCompositing =
1608 gfxConfig::GetFeature(gfx::Feature::HW_COMPOSITING);
1609 InitFeatureObject(aCx, aObj, "hwCompositing", hwCompositing, &obj);
1610
1611 gfx::FeatureState& gpuProcess =
1612 gfxConfig::GetFeature(gfx::Feature::GPU_PROCESS);
1613 InitFeatureObject(aCx, aObj, "gpuProcess", gpuProcess, &obj);
1614
1615 gfx::FeatureState& wrQualified =
1616 gfxConfig::GetFeature(gfx::Feature::WEBRENDER_QUALIFIED);
1617 InitFeatureObject(aCx, aObj, "wrQualified", wrQualified, &obj);
1618
1619 gfx::FeatureState& webrender = gfxConfig::GetFeature(gfx::Feature::WEBRENDER);
1620 InitFeatureObject(aCx, aObj, "webrender", webrender, &obj);
1621
1622 gfx::FeatureState& wrCompositor = gfxConfig::GetFeature(gfx::Feature::WEBRENDER_COMPOSITOR);
1623 InitFeatureObject(aCx, aObj, "wrCompositor", wrCompositor, &obj);
1624
1625 // Only include AL if the platform attempted to use it.
1626 gfx::FeatureState& advancedLayers =
1627 gfxConfig::GetFeature(gfx::Feature::ADVANCED_LAYERS);
1628 if (advancedLayers.GetValue() != FeatureStatus::Unused) {
1629 InitFeatureObject(aCx, aObj, "advancedLayers", advancedLayers, &obj);
1630
1631 if (gfxConfig::UseFallback(Fallback::NO_CONSTANT_BUFFER_OFFSETTING)) {
1632 JS::Rooted<JS::Value> trueVal(aCx, JS::BooleanValue(true));
1633 JS_SetProperty(aCx, obj, "noConstantBufferOffsetting", trueVal);
1634 }
1635 }
1636 }
1637
InitFeatureObject(JSContext * aCx,JS::Handle<JSObject * > aContainer,const char * aName,mozilla::gfx::FeatureState & aFeatureState,JS::MutableHandle<JSObject * > aOutObj)1638 bool GfxInfoBase::InitFeatureObject(JSContext* aCx,
1639 JS::Handle<JSObject*> aContainer,
1640 const char* aName,
1641 mozilla::gfx::FeatureState& aFeatureState,
1642 JS::MutableHandle<JSObject*> aOutObj) {
1643 JS::Rooted<JSObject*> obj(aCx, JS_NewPlainObject(aCx));
1644 if (!obj) {
1645 return false;
1646 }
1647
1648 nsCString status;
1649 auto value = aFeatureState.GetValue();
1650 if (value == FeatureStatus::Blacklisted ||
1651 value == FeatureStatus::Unavailable || value == FeatureStatus::Blocked) {
1652 status.AppendPrintf("%s:%s", FeatureStatusToString(value),
1653 aFeatureState.GetFailureId().get());
1654 } else {
1655 status.Append(FeatureStatusToString(value));
1656 }
1657
1658 JS::Rooted<JSString*> str(aCx, JS_NewStringCopyZ(aCx, status.get()));
1659 JS::Rooted<JS::Value> val(aCx, JS::StringValue(str));
1660 JS_SetProperty(aCx, obj, "status", val);
1661
1662 // Add the feature object to the container.
1663 {
1664 JS::Rooted<JS::Value> val(aCx, JS::ObjectValue(*obj));
1665 JS_SetProperty(aCx, aContainer, aName, val);
1666 }
1667
1668 aOutObj.set(obj);
1669 return true;
1670 }
1671
GetActiveCrashGuards(JSContext * aCx,JS::MutableHandle<JS::Value> aOut)1672 nsresult GfxInfoBase::GetActiveCrashGuards(JSContext* aCx,
1673 JS::MutableHandle<JS::Value> aOut) {
1674 JS::Rooted<JSObject*> array(aCx, JS::NewArrayObject(aCx, 0));
1675 if (!array) {
1676 return NS_ERROR_OUT_OF_MEMORY;
1677 }
1678 aOut.setObject(*array);
1679
1680 DriverCrashGuard::ForEachActiveCrashGuard(
1681 [&](const char* aName, const char* aPrefName) -> void {
1682 JS::Rooted<JSObject*> obj(aCx, JS_NewPlainObject(aCx));
1683 if (!obj) {
1684 return;
1685 }
1686 if (!SetJSPropertyString(aCx, obj, "type", aName)) {
1687 return;
1688 }
1689 if (!SetJSPropertyString(aCx, obj, "prefName", aPrefName)) {
1690 return;
1691 }
1692 if (!AppendJSElement(aCx, array, obj)) {
1693 return;
1694 }
1695 });
1696
1697 return NS_OK;
1698 }
1699
1700 NS_IMETHODIMP
GetWebRenderEnabled(bool * aWebRenderEnabled)1701 GfxInfoBase::GetWebRenderEnabled(bool* aWebRenderEnabled) {
1702 *aWebRenderEnabled = gfxVars::UseWebRender();
1703 return NS_OK;
1704 }
1705
1706 NS_IMETHODIMP
GetUsesTiling(bool * aUsesTiling)1707 GfxInfoBase::GetUsesTiling(bool* aUsesTiling) {
1708 *aUsesTiling = gfxPlatform::GetPlatform()->UsesTiling();
1709 return NS_OK;
1710 }
1711
1712 NS_IMETHODIMP
GetContentUsesTiling(bool * aUsesTiling)1713 GfxInfoBase::GetContentUsesTiling(bool* aUsesTiling) {
1714 *aUsesTiling = gfxPlatform::GetPlatform()->ContentUsesTiling();
1715 return NS_OK;
1716 }
1717
1718 NS_IMETHODIMP
GetOffMainThreadPaintEnabled(bool * aOffMainThreadPaintEnabled)1719 GfxInfoBase::GetOffMainThreadPaintEnabled(bool* aOffMainThreadPaintEnabled) {
1720 *aOffMainThreadPaintEnabled = gfxConfig::IsEnabled(gfx::Feature::OMTP);
1721 return NS_OK;
1722 }
1723
1724 NS_IMETHODIMP
GetOffMainThreadPaintWorkerCount(int32_t * aOffMainThreadPaintWorkerCount)1725 GfxInfoBase::GetOffMainThreadPaintWorkerCount(
1726 int32_t* aOffMainThreadPaintWorkerCount) {
1727 if (gfxConfig::IsEnabled(gfx::Feature::OMTP)) {
1728 *aOffMainThreadPaintWorkerCount =
1729 layers::PaintThread::CalculatePaintWorkerCount();
1730 } else {
1731 *aOffMainThreadPaintWorkerCount = 0;
1732 }
1733 return NS_OK;
1734 }
1735
1736 NS_IMETHODIMP
GetTargetFrameRate(uint32_t * aTargetFrameRate)1737 GfxInfoBase::GetTargetFrameRate(uint32_t* aTargetFrameRate) {
1738 *aTargetFrameRate = gfxPlatform::TargetFrameRate();
1739 return NS_OK;
1740 }
1741
1742 NS_IMETHODIMP
GetIsHeadless(bool * aIsHeadless)1743 GfxInfoBase::GetIsHeadless(bool* aIsHeadless) {
1744 *aIsHeadless = gfxPlatform::IsHeadless();
1745 return NS_OK;
1746 }
1747
1748 NS_IMETHODIMP
GetContentBackend(nsAString & aContentBackend)1749 GfxInfoBase::GetContentBackend(nsAString& aContentBackend) {
1750 BackendType backend = gfxPlatform::GetPlatform()->GetDefaultContentBackend();
1751 nsString outStr;
1752
1753 switch (backend) {
1754 case BackendType::DIRECT2D1_1: {
1755 outStr.AppendPrintf("Direct2D 1.1");
1756 break;
1757 }
1758 case BackendType::SKIA: {
1759 outStr.AppendPrintf("Skia");
1760 break;
1761 }
1762 case BackendType::CAIRO: {
1763 outStr.AppendPrintf("Cairo");
1764 break;
1765 }
1766 default:
1767 return NS_ERROR_FAILURE;
1768 }
1769
1770 aContentBackend.Assign(outStr);
1771 return NS_OK;
1772 }
1773
1774 NS_IMETHODIMP
GetUsingGPUProcess(bool * aOutValue)1775 GfxInfoBase::GetUsingGPUProcess(bool* aOutValue) {
1776 GPUProcessManager* gpu = GPUProcessManager::Get();
1777 if (!gpu) {
1778 // Not supported in content processes.
1779 return NS_ERROR_FAILURE;
1780 }
1781
1782 *aOutValue = !!gpu->GetGPUChild();
1783 return NS_OK;
1784 }
1785
1786 NS_IMETHODIMP
ControlGPUProcessForXPCShell(bool aEnable,bool * _retval)1787 GfxInfoBase::ControlGPUProcessForXPCShell(bool aEnable, bool* _retval) {
1788 gfxPlatform::GetPlatform();
1789
1790 GPUProcessManager* gpm = GPUProcessManager::Get();
1791 if (aEnable) {
1792 if (!gfxConfig::IsEnabled(gfx::Feature::GPU_PROCESS)) {
1793 gfxConfig::UserForceEnable(gfx::Feature::GPU_PROCESS, "xpcshell-test");
1794 }
1795 gpm->LaunchGPUProcess();
1796 gpm->EnsureGPUReady();
1797 } else {
1798 gfxConfig::UserDisable(gfx::Feature::GPU_PROCESS, "xpcshell-test");
1799 gpm->KillProcess();
1800 }
1801
1802 *_retval = true;
1803 return NS_OK;
1804 }
1805
GfxInfoCollectorBase()1806 GfxInfoCollectorBase::GfxInfoCollectorBase() {
1807 GfxInfoBase::AddCollector(this);
1808 }
1809
~GfxInfoCollectorBase()1810 GfxInfoCollectorBase::~GfxInfoCollectorBase() {
1811 GfxInfoBase::RemoveCollector(this);
1812 }
1813