Line data Source code
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 "GfxInfoWebGL.h"
13 : #include "GfxDriverInfo.h"
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 "nsIDOMElement.h"
24 : #include "nsIDOMHTMLCollection.h"
25 : #include "nsIDOMNode.h"
26 : #include "nsIDOMNodeList.h"
27 : #include "nsTArray.h"
28 : #include "nsXULAppAPI.h"
29 : #include "nsIXULAppInfo.h"
30 : #include "mozilla/Preferences.h"
31 : #include "mozilla/dom/ContentChild.h"
32 : #include "mozilla/gfx/2D.h"
33 : #include "mozilla/gfx/GPUProcessManager.h"
34 : #include "mozilla/gfx/Logging.h"
35 : #include "mozilla/gfx/gfxVars.h"
36 : #include "MediaPrefs.h"
37 : #include "gfxPrefs.h"
38 : #include "gfxPlatform.h"
39 : #include "gfxConfig.h"
40 : #include "DriverCrashGuard.h"
41 :
42 : #if defined(MOZ_CRASHREPORTER)
43 : #include "nsExceptionHandler.h"
44 : #endif
45 :
46 : using namespace mozilla::widget;
47 : using namespace mozilla;
48 : using mozilla::MutexAutoLock;
49 :
50 : nsTArray<GfxDriverInfo>* GfxInfoBase::mDriverInfo;
51 : nsTArray<dom::GfxInfoFeatureStatus>* GfxInfoBase::mFeatureStatus;
52 : bool GfxInfoBase::mDriverInfoObserverInitialized;
53 : bool GfxInfoBase::mShutdownOccurred;
54 :
55 : // Observes for shutdown so that the child GfxDriverInfo list is freed.
56 : class ShutdownObserver : public nsIObserver
57 : {
58 0 : virtual ~ShutdownObserver() {}
59 :
60 : public:
61 3 : ShutdownObserver() {}
62 :
63 : NS_DECL_ISUPPORTS
64 :
65 0 : NS_IMETHOD Observe(nsISupports *subject, const char *aTopic,
66 : const char16_t *aData) override
67 : {
68 0 : MOZ_ASSERT(strcmp(aTopic, NS_XPCOM_SHUTDOWN_OBSERVER_ID) == 0);
69 :
70 0 : delete GfxInfoBase::mDriverInfo;
71 0 : GfxInfoBase::mDriverInfo = nullptr;
72 :
73 0 : delete GfxInfoBase::mFeatureStatus;
74 0 : GfxInfoBase::mFeatureStatus = nullptr;
75 :
76 0 : for (uint32_t i = 0; i < DeviceFamilyMax; i++)
77 0 : delete GfxDriverInfo::mDeviceFamilies[i];
78 :
79 0 : for (uint32_t i = 0; i < DeviceVendorMax; i++)
80 0 : delete GfxDriverInfo::mDeviceVendors[i];
81 :
82 0 : GfxInfoBase::mShutdownOccurred = true;
83 :
84 0 : return NS_OK;
85 : }
86 : };
87 :
88 3 : NS_IMPL_ISUPPORTS(ShutdownObserver, nsIObserver)
89 :
90 3 : void InitGfxDriverInfoShutdownObserver()
91 : {
92 3 : if (GfxInfoBase::mDriverInfoObserverInitialized)
93 0 : return;
94 :
95 3 : GfxInfoBase::mDriverInfoObserverInitialized = true;
96 :
97 6 : nsCOMPtr<nsIObserverService> observerService = services::GetObserverService();
98 3 : if (!observerService) {
99 0 : NS_WARNING("Could not get observer service!");
100 0 : return;
101 : }
102 :
103 3 : ShutdownObserver *obs = new ShutdownObserver();
104 3 : observerService->AddObserver(obs, NS_XPCOM_SHUTDOWN_OBSERVER_ID, false);
105 : }
106 :
107 : using namespace mozilla::widget;
108 : using namespace mozilla::gfx;
109 : using namespace mozilla;
110 :
111 221 : NS_IMPL_ISUPPORTS(GfxInfoBase, nsIGfxInfo, nsIObserver, nsISupportsWeakReference)
112 :
113 : #define BLACKLIST_PREF_BRANCH "gfx.blacklist."
114 : #define SUGGESTED_VERSION_PREF BLACKLIST_PREF_BRANCH "suggested-driver-version"
115 : #define BLACKLIST_ENTRY_TAG_NAME "gfxBlacklistEntry"
116 :
117 : static const char*
118 50 : GetPrefNameForFeature(int32_t aFeature)
119 : {
120 50 : const char* name = nullptr;
121 50 : switch(aFeature) {
122 : case nsIGfxInfo::FEATURE_DIRECT2D:
123 2 : name = BLACKLIST_PREF_BRANCH "direct2d";
124 2 : break;
125 : case nsIGfxInfo::FEATURE_DIRECT3D_9_LAYERS:
126 2 : name = BLACKLIST_PREF_BRANCH "layers.direct3d9";
127 2 : break;
128 : case nsIGfxInfo::FEATURE_DIRECT3D_10_LAYERS:
129 2 : name = BLACKLIST_PREF_BRANCH "layers.direct3d10";
130 2 : break;
131 : case nsIGfxInfo::FEATURE_DIRECT3D_10_1_LAYERS:
132 2 : name = BLACKLIST_PREF_BRANCH "layers.direct3d10-1";
133 2 : break;
134 : case nsIGfxInfo::FEATURE_DIRECT3D_11_LAYERS:
135 2 : name = BLACKLIST_PREF_BRANCH "layers.direct3d11";
136 2 : break;
137 : case nsIGfxInfo::FEATURE_DIRECT3D_11_ANGLE:
138 2 : name = BLACKLIST_PREF_BRANCH "direct3d11angle";
139 2 : break;
140 : case nsIGfxInfo::FEATURE_HARDWARE_VIDEO_DECODING:
141 5 : name = BLACKLIST_PREF_BRANCH "hardwarevideodecoding";
142 5 : break;
143 : case nsIGfxInfo::FEATURE_OPENGL_LAYERS:
144 2 : name = BLACKLIST_PREF_BRANCH "layers.opengl";
145 2 : break;
146 : case nsIGfxInfo::FEATURE_WEBGL_OPENGL:
147 2 : name = BLACKLIST_PREF_BRANCH "webgl.opengl";
148 2 : break;
149 : case nsIGfxInfo::FEATURE_WEBGL_ANGLE:
150 2 : name = BLACKLIST_PREF_BRANCH "webgl.angle";
151 2 : break;
152 : case nsIGfxInfo::FEATURE_WEBGL_MSAA:
153 2 : name = BLACKLIST_PREF_BRANCH "webgl.msaa";
154 2 : break;
155 : case nsIGfxInfo::FEATURE_STAGEFRIGHT:
156 2 : name = BLACKLIST_PREF_BRANCH "stagefright";
157 2 : break;
158 : case nsIGfxInfo::FEATURE_WEBRTC_HW_ACCELERATION:
159 2 : name = BLACKLIST_PREF_BRANCH "webrtc.hw.acceleration";
160 2 : break;
161 : case nsIGfxInfo::FEATURE_WEBRTC_HW_ACCELERATION_ENCODE:
162 2 : name = BLACKLIST_PREF_BRANCH "webrtc.hw.acceleration.encode";
163 2 : break;
164 : case nsIGfxInfo::FEATURE_WEBRTC_HW_ACCELERATION_DECODE:
165 2 : name = BLACKLIST_PREF_BRANCH "webrtc.hw.acceleration.decode";
166 2 : break;
167 : case nsIGfxInfo::FEATURE_CANVAS2D_ACCELERATION:
168 2 : name = BLACKLIST_PREF_BRANCH "canvas2d.acceleration";
169 2 : break;
170 : case nsIGfxInfo::FEATURE_WEBGL2:
171 2 : name = BLACKLIST_PREF_BRANCH "webgl2";
172 2 : break;
173 : case nsIGfxInfo::FEATURE_ADVANCED_LAYERS:
174 2 : name = BLACKLIST_PREF_BRANCH "layers.advanced";
175 2 : break;
176 : case nsIGfxInfo::FEATURE_VP8_HW_DECODE:
177 : case nsIGfxInfo::FEATURE_VP9_HW_DECODE:
178 : case nsIGfxInfo::FEATURE_DX_INTEROP2:
179 : case nsIGfxInfo::FEATURE_GPU_PROCESS:
180 : // We don't provide prefs for these features.
181 11 : break;
182 : default:
183 0 : MOZ_ASSERT_UNREACHABLE("Unexpected nsIGfxInfo feature?!");
184 : break;
185 : }
186 :
187 50 : return name;
188 : }
189 :
190 : // Returns the value of the pref for the relevant feature in aValue.
191 : // If the pref doesn't exist, aValue is not touched, and returns false.
192 : static bool
193 50 : GetPrefValueForFeature(int32_t aFeature, int32_t& aValue, nsACString& aFailureId)
194 : {
195 50 : const char *prefname = GetPrefNameForFeature(aFeature);
196 50 : if (!prefname)
197 11 : return false;
198 :
199 39 : aValue = nsIGfxInfo::FEATURE_STATUS_UNKNOWN;
200 39 : if (!NS_SUCCEEDED(Preferences::GetInt(prefname, &aValue))) {
201 39 : return false;
202 : }
203 :
204 0 : nsCString failureprefname(prefname);
205 0 : failureprefname += ".failureid";
206 0 : nsAdoptingCString failureValue = Preferences::GetCString(failureprefname.get());
207 0 : if (failureValue) {
208 0 : aFailureId = failureValue.get();
209 : } else {
210 0 : aFailureId = "FEATURE_FAILURE_BLACKLIST_PREF";
211 : }
212 :
213 0 : return true;
214 : }
215 :
216 : static void
217 0 : SetPrefValueForFeature(int32_t aFeature, int32_t aValue, const nsACString& aFailureId)
218 : {
219 0 : const char *prefname = GetPrefNameForFeature(aFeature);
220 0 : if (!prefname)
221 0 : return;
222 :
223 0 : Preferences::SetInt(prefname, aValue);
224 0 : if (!aFailureId.IsEmpty()) {
225 0 : nsCString failureprefname(prefname);
226 0 : failureprefname += ".failureid";
227 0 : Preferences::SetCString(failureprefname.get(), aFailureId);
228 : }
229 : }
230 :
231 : static void
232 0 : RemovePrefForFeature(int32_t aFeature)
233 : {
234 0 : const char *prefname = GetPrefNameForFeature(aFeature);
235 0 : if (!prefname)
236 0 : return;
237 :
238 0 : Preferences::ClearUser(prefname);
239 : }
240 :
241 : static bool
242 0 : GetPrefValueForDriverVersion(nsCString& aVersion)
243 : {
244 0 : return NS_SUCCEEDED(Preferences::GetCString(SUGGESTED_VERSION_PREF,
245 : &aVersion));
246 : }
247 :
248 : static void
249 0 : SetPrefValueForDriverVersion(const nsAString& aVersion)
250 : {
251 0 : Preferences::SetString(SUGGESTED_VERSION_PREF, aVersion);
252 0 : }
253 :
254 : static void
255 0 : RemovePrefForDriverVersion()
256 : {
257 0 : Preferences::ClearUser(SUGGESTED_VERSION_PREF);
258 0 : }
259 :
260 :
261 : static OperatingSystem
262 0 : BlacklistOSToOperatingSystem(const nsAString& os)
263 : {
264 0 : if (os.EqualsLiteral("WINNT 6.1"))
265 0 : return OperatingSystem::Windows7;
266 0 : else if (os.EqualsLiteral("WINNT 6.2"))
267 0 : return OperatingSystem::Windows8;
268 0 : else if (os.EqualsLiteral("WINNT 6.3"))
269 0 : return OperatingSystem::Windows8_1;
270 0 : else if (os.EqualsLiteral("WINNT 10.0"))
271 0 : return OperatingSystem::Windows10;
272 0 : else if (os.EqualsLiteral("Linux"))
273 0 : return OperatingSystem::Linux;
274 0 : else if (os.EqualsLiteral("Darwin 9"))
275 0 : return OperatingSystem::OSX10_5;
276 0 : else if (os.EqualsLiteral("Darwin 10"))
277 0 : return OperatingSystem::OSX10_6;
278 0 : else if (os.EqualsLiteral("Darwin 11"))
279 0 : return OperatingSystem::OSX10_7;
280 0 : else if (os.EqualsLiteral("Darwin 12"))
281 0 : return OperatingSystem::OSX10_8;
282 0 : else if (os.EqualsLiteral("Darwin 13"))
283 0 : return OperatingSystem::OSX10_9;
284 0 : else if (os.EqualsLiteral("Darwin 14"))
285 0 : return OperatingSystem::OSX10_10;
286 0 : else if (os.EqualsLiteral("Darwin 15"))
287 0 : return OperatingSystem::OSX10_11;
288 0 : else if (os.EqualsLiteral("Darwin 16"))
289 0 : return OperatingSystem::OSX10_12;
290 0 : else if (os.EqualsLiteral("Android"))
291 0 : return OperatingSystem::Android;
292 : // For historical reasons, "All" in blocklist means "All Windows"
293 0 : else if (os.EqualsLiteral("All"))
294 0 : return OperatingSystem::Windows;
295 :
296 0 : return OperatingSystem::Unknown;
297 : }
298 :
299 : static GfxDeviceFamily*
300 0 : BlacklistDevicesToDeviceFamily(nsTArray<nsCString>& devices)
301 : {
302 0 : if (devices.Length() == 0)
303 0 : return nullptr;
304 :
305 : // For each device, get its device ID, and return a freshly-allocated
306 : // GfxDeviceFamily with the contents of that array.
307 0 : GfxDeviceFamily* deviceIds = new GfxDeviceFamily;
308 :
309 0 : for (uint32_t i = 0; i < devices.Length(); ++i) {
310 : // We make sure we don't add any "empty" device entries to the array, so
311 : // we don't need to check if devices[i] is empty.
312 0 : deviceIds->AppendElement(NS_ConvertUTF8toUTF16(devices[i]));
313 : }
314 :
315 0 : return deviceIds;
316 : }
317 :
318 : static int32_t
319 0 : BlacklistFeatureToGfxFeature(const nsAString& aFeature)
320 : {
321 0 : MOZ_ASSERT(!aFeature.IsEmpty());
322 0 : if (aFeature.EqualsLiteral("DIRECT2D"))
323 0 : return nsIGfxInfo::FEATURE_DIRECT2D;
324 0 : else if (aFeature.EqualsLiteral("DIRECT3D_9_LAYERS"))
325 0 : return nsIGfxInfo::FEATURE_DIRECT3D_9_LAYERS;
326 0 : else if (aFeature.EqualsLiteral("DIRECT3D_10_LAYERS"))
327 0 : return nsIGfxInfo::FEATURE_DIRECT3D_10_LAYERS;
328 0 : else if (aFeature.EqualsLiteral("DIRECT3D_10_1_LAYERS"))
329 0 : return nsIGfxInfo::FEATURE_DIRECT3D_10_1_LAYERS;
330 0 : else if (aFeature.EqualsLiteral("DIRECT3D_11_LAYERS"))
331 0 : return nsIGfxInfo::FEATURE_DIRECT3D_11_LAYERS;
332 0 : else if (aFeature.EqualsLiteral("DIRECT3D_11_ANGLE"))
333 0 : return nsIGfxInfo::FEATURE_DIRECT3D_11_ANGLE;
334 0 : else if (aFeature.EqualsLiteral("HARDWARE_VIDEO_DECODING"))
335 0 : return nsIGfxInfo::FEATURE_HARDWARE_VIDEO_DECODING;
336 0 : else if (aFeature.EqualsLiteral("OPENGL_LAYERS"))
337 0 : return nsIGfxInfo::FEATURE_OPENGL_LAYERS;
338 0 : else if (aFeature.EqualsLiteral("WEBGL_OPENGL"))
339 0 : return nsIGfxInfo::FEATURE_WEBGL_OPENGL;
340 0 : else if (aFeature.EqualsLiteral("WEBGL_ANGLE"))
341 0 : return nsIGfxInfo::FEATURE_WEBGL_ANGLE;
342 0 : else if (aFeature.EqualsLiteral("WEBGL_MSAA"))
343 0 : return nsIGfxInfo::FEATURE_WEBGL_MSAA;
344 0 : else if (aFeature.EqualsLiteral("STAGEFRIGHT"))
345 0 : return nsIGfxInfo::FEATURE_STAGEFRIGHT;
346 0 : else if (aFeature.EqualsLiteral("WEBRTC_HW_ACCELERATION_ENCODE"))
347 0 : return nsIGfxInfo::FEATURE_WEBRTC_HW_ACCELERATION_ENCODE;
348 0 : else if (aFeature.EqualsLiteral("WEBRTC_HW_ACCELERATION_DECODE"))
349 0 : return nsIGfxInfo::FEATURE_WEBRTC_HW_ACCELERATION_DECODE;
350 0 : else if (aFeature.EqualsLiteral("WEBRTC_HW_ACCELERATION"))
351 0 : return nsIGfxInfo::FEATURE_WEBRTC_HW_ACCELERATION;
352 0 : else if (aFeature.EqualsLiteral("CANVAS2D_ACCELERATION"))
353 0 : return nsIGfxInfo::FEATURE_CANVAS2D_ACCELERATION;
354 0 : else if (aFeature.EqualsLiteral("WEBGL2"))
355 0 : return nsIGfxInfo::FEATURE_WEBGL2;
356 :
357 : // If we don't recognize the feature, it may be new, and something
358 : // this version doesn't understand. So, nothing to do. This is
359 : // different from feature not being specified at all, in which case
360 : // this method should not get called and we should continue with the
361 : // "all features" blocklisting.
362 0 : return -1;
363 : }
364 :
365 : static int32_t
366 0 : BlacklistFeatureStatusToGfxFeatureStatus(const nsAString& aStatus)
367 : {
368 0 : if (aStatus.EqualsLiteral("STATUS_OK"))
369 0 : return nsIGfxInfo::FEATURE_STATUS_OK;
370 0 : else if (aStatus.EqualsLiteral("BLOCKED_DRIVER_VERSION"))
371 0 : return nsIGfxInfo::FEATURE_BLOCKED_DRIVER_VERSION;
372 0 : else if (aStatus.EqualsLiteral("BLOCKED_DEVICE"))
373 0 : return nsIGfxInfo::FEATURE_BLOCKED_DEVICE;
374 0 : else if (aStatus.EqualsLiteral("DISCOURAGED"))
375 0 : return nsIGfxInfo::FEATURE_DISCOURAGED;
376 0 : else if (aStatus.EqualsLiteral("BLOCKED_OS_VERSION"))
377 0 : return nsIGfxInfo::FEATURE_BLOCKED_OS_VERSION;
378 :
379 : // Do not allow it to set STATUS_UNKNOWN. Also, we are not
380 : // expecting the "mismatch" status showing up here.
381 :
382 0 : return nsIGfxInfo::FEATURE_STATUS_OK;
383 : }
384 :
385 : static VersionComparisonOp
386 0 : BlacklistComparatorToComparisonOp(const nsAString& op)
387 : {
388 0 : if (op.EqualsLiteral("LESS_THAN"))
389 0 : return DRIVER_LESS_THAN;
390 0 : else if (op.EqualsLiteral("BUILD_ID_LESS_THAN"))
391 0 : return DRIVER_BUILD_ID_LESS_THAN;
392 0 : else if (op.EqualsLiteral("LESS_THAN_OR_EQUAL"))
393 0 : return DRIVER_LESS_THAN_OR_EQUAL;
394 0 : else if (op.EqualsLiteral("BUILD_ID_LESS_THAN_OR_EQUAL"))
395 0 : return DRIVER_BUILD_ID_LESS_THAN_OR_EQUAL;
396 0 : else if (op.EqualsLiteral("GREATER_THAN"))
397 0 : return DRIVER_GREATER_THAN;
398 0 : else if (op.EqualsLiteral("GREATER_THAN_OR_EQUAL"))
399 0 : return DRIVER_GREATER_THAN_OR_EQUAL;
400 0 : else if (op.EqualsLiteral("EQUAL"))
401 0 : return DRIVER_EQUAL;
402 0 : else if (op.EqualsLiteral("NOT_EQUAL"))
403 0 : return DRIVER_NOT_EQUAL;
404 0 : else if (op.EqualsLiteral("BETWEEN_EXCLUSIVE"))
405 0 : return DRIVER_BETWEEN_EXCLUSIVE;
406 0 : else if (op.EqualsLiteral("BETWEEN_INCLUSIVE"))
407 0 : return DRIVER_BETWEEN_INCLUSIVE;
408 0 : else if (op.EqualsLiteral("BETWEEN_INCLUSIVE_START"))
409 0 : return DRIVER_BETWEEN_INCLUSIVE_START;
410 :
411 0 : return DRIVER_COMPARISON_IGNORED;
412 : }
413 :
414 :
415 : /*
416 : Deserialize Blacklist entries from string.
417 : e.g:
418 : 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
419 : */
420 : static bool
421 0 : BlacklistEntryToDriverInfo(nsCString& aBlacklistEntry,
422 : GfxDriverInfo& aDriverInfo)
423 : {
424 : // If we get an application version to be zero, something is not working
425 : // and we are not going to bother checking the blocklist versions.
426 : // See TestGfxWidgets.cpp for how version comparison works.
427 : // <versionRange minVersion="42.0a1" maxVersion="45.0"></versionRange>
428 0 : static mozilla::Version zeroV("0");
429 0 : static mozilla::Version appV(GfxInfoBase::GetApplicationVersion().get());
430 0 : if (appV <= zeroV) {
431 0 : gfxCriticalErrorOnce(gfxCriticalError::DefaultOptions(false)) << "Invalid application version " << GfxInfoBase::GetApplicationVersion().get();
432 : }
433 :
434 0 : nsTArray<nsCString> keyValues;
435 0 : ParseString(aBlacklistEntry, '\t', keyValues);
436 :
437 0 : aDriverInfo.mRuleId = NS_LITERAL_CSTRING("FEATURE_FAILURE_DL_BLACKLIST_NO_ID");
438 :
439 0 : for (uint32_t i = 0; i < keyValues.Length(); ++i) {
440 0 : nsCString keyValue = keyValues[i];
441 0 : nsTArray<nsCString> splitted;
442 0 : ParseString(keyValue, ':', splitted);
443 0 : if (splitted.Length() != 2) {
444 : // If we don't recognize the input data, we do not want to proceed.
445 0 : gfxCriticalErrorOnce(CriticalLog::DefaultOptions(false)) << "Unrecognized data " << keyValue.get();
446 0 : return false;
447 : }
448 0 : nsCString key = splitted[0];
449 0 : nsCString value = splitted[1];
450 0 : NS_ConvertUTF8toUTF16 dataValue(value);
451 :
452 0 : if (value.Length() == 0) {
453 : // Safety check for empty values.
454 0 : gfxCriticalErrorOnce(CriticalLog::DefaultOptions(false)) << "Empty value for " << key.get();
455 0 : return false;
456 : }
457 :
458 0 : if (key.EqualsLiteral("blockID")) {
459 0 : nsCString blockIdStr = NS_LITERAL_CSTRING("FEATURE_FAILURE_DL_BLACKLIST_") + value;
460 0 : aDriverInfo.mRuleId = blockIdStr.get();
461 0 : } else if (key.EqualsLiteral("os")) {
462 0 : aDriverInfo.mOperatingSystem = BlacklistOSToOperatingSystem(dataValue);
463 0 : } else if (key.EqualsLiteral("osversion")) {
464 0 : aDriverInfo.mOperatingSystemVersion = strtoul(value.get(), nullptr, 10);
465 0 : } else if (key.EqualsLiteral("vendor")) {
466 0 : aDriverInfo.mAdapterVendor = dataValue;
467 0 : } else if (key.EqualsLiteral("feature")) {
468 0 : aDriverInfo.mFeature = BlacklistFeatureToGfxFeature(dataValue);
469 0 : if (aDriverInfo.mFeature < 0) {
470 : // If we don't recognize the feature, we do not want to proceed.
471 0 : gfxCriticalErrorOnce(CriticalLog::DefaultOptions(false)) << "Unrecognized feature " << value.get();
472 0 : return false;
473 : }
474 0 : } else if (key.EqualsLiteral("featureStatus")) {
475 0 : aDriverInfo.mFeatureStatus = BlacklistFeatureStatusToGfxFeatureStatus(dataValue);
476 0 : } else if (key.EqualsLiteral("driverVersion")) {
477 : uint64_t version;
478 0 : if (ParseDriverVersion(dataValue, &version))
479 0 : aDriverInfo.mDriverVersion = version;
480 0 : } else if (key.EqualsLiteral("driverVersionMax")) {
481 : uint64_t version;
482 0 : if (ParseDriverVersion(dataValue, &version))
483 0 : aDriverInfo.mDriverVersionMax = version;
484 0 : } else if (key.EqualsLiteral("driverVersionComparator")) {
485 0 : aDriverInfo.mComparisonOp = BlacklistComparatorToComparisonOp(dataValue);
486 0 : } else if (key.EqualsLiteral("model")) {
487 0 : aDriverInfo.mModel = dataValue;
488 0 : } else if (key.EqualsLiteral("product")) {
489 0 : aDriverInfo.mProduct = dataValue;
490 0 : } else if (key.EqualsLiteral("manufacturer")) {
491 0 : aDriverInfo.mManufacturer = dataValue;
492 0 : } else if (key.EqualsLiteral("hardware")) {
493 0 : aDriverInfo.mHardware = dataValue;
494 0 : } else if (key.EqualsLiteral("versionRange")) {
495 0 : nsTArray<nsCString> versionRange;
496 0 : ParseString(value, ',', versionRange);
497 0 : if (versionRange.Length() != 2) {
498 0 : gfxCriticalErrorOnce(CriticalLog::DefaultOptions(false)) << "Unrecognized versionRange " << value.get();
499 0 : return false;
500 : }
501 0 : nsCString minValue = versionRange[0];
502 0 : nsCString maxValue = versionRange[1];
503 :
504 0 : mozilla::Version minV(minValue.get());
505 0 : mozilla::Version maxV(maxValue.get());
506 :
507 0 : if (minV > zeroV && !(appV >= minV)) {
508 : // The version of the application is less than the minimal version
509 : // this blocklist entry applies to, so we can just ignore it by
510 : // returning false and letting the caller deal with it.
511 0 : return false;
512 : }
513 0 : if (maxV > zeroV && !(appV <= maxV)) {
514 : // The version of the application is more than the maximal version
515 : // this blocklist entry applies to, so we can just ignore it by
516 : // returning false and letting the caller deal with it.
517 0 : return false;
518 : }
519 0 : } else if (key.EqualsLiteral("devices")) {
520 0 : nsTArray<nsCString> devices;
521 0 : ParseString(value, ',', devices);
522 0 : GfxDeviceFamily* deviceIds = BlacklistDevicesToDeviceFamily(devices);
523 0 : if (deviceIds) {
524 : // Get GfxDriverInfo to adopt the devices array we created.
525 0 : aDriverInfo.mDeleteDevices = true;
526 0 : aDriverInfo.mDevices = deviceIds;
527 : }
528 : }
529 : // We explicitly ignore unknown elements.
530 : }
531 :
532 0 : return true;
533 : }
534 :
535 : static void
536 0 : BlacklistEntriesToDriverInfo(nsTArray<nsCString>& aBlacklistEntries,
537 : nsTArray<GfxDriverInfo>& aDriverInfo)
538 : {
539 0 : aDriverInfo.Clear();
540 0 : aDriverInfo.SetLength(aBlacklistEntries.Length());
541 :
542 0 : for (uint32_t i = 0; i < aBlacklistEntries.Length(); ++i) {
543 0 : nsCString blacklistEntry = aBlacklistEntries[i];
544 0 : GfxDriverInfo di;
545 0 : if (BlacklistEntryToDriverInfo(blacklistEntry, di)) {
546 0 : aDriverInfo[i] = di;
547 : // Prevent di falling out of scope from destroying the devices.
548 0 : di.mDeleteDevices = false;
549 : }
550 : }
551 0 : }
552 :
553 : NS_IMETHODIMP
554 0 : GfxInfoBase::Observe(nsISupports* aSubject, const char* aTopic,
555 : const char16_t* aData)
556 : {
557 0 : if (strcmp(aTopic, "blocklist-data-gfxItems") == 0) {
558 0 : nsTArray<GfxDriverInfo> driverInfo;
559 0 : nsTArray<nsCString> blacklistEntries;
560 0 : nsCString utf8Data = NS_ConvertUTF16toUTF8(aData);
561 0 : if (utf8Data.Length() > 0) {
562 0 : ParseString(utf8Data, '\n', blacklistEntries);
563 : }
564 0 : BlacklistEntriesToDriverInfo(blacklistEntries, driverInfo);
565 0 : EvaluateDownloadedBlacklist(driverInfo);
566 : }
567 :
568 0 : return NS_OK;
569 : }
570 :
571 3 : GfxInfoBase::GfxInfoBase()
572 3 : : mMutex("GfxInfoBase")
573 : {
574 3 : }
575 :
576 0 : GfxInfoBase::~GfxInfoBase()
577 : {
578 0 : }
579 :
580 : nsresult
581 3 : GfxInfoBase::Init()
582 : {
583 3 : InitGfxDriverInfoShutdownObserver();
584 3 : gfxPrefs::GetSingleton();
585 3 : MediaPrefs::GetSingleton();
586 :
587 6 : nsCOMPtr<nsIObserverService> os = mozilla::services::GetObserverService();
588 3 : if (os) {
589 3 : os->AddObserver(this, "blocklist-data-gfxItems", true);
590 : }
591 :
592 6 : return NS_OK;
593 : }
594 :
595 : NS_IMETHODIMP
596 50 : GfxInfoBase::GetFeatureStatus(int32_t aFeature, nsACString& aFailureId, int32_t* aStatus)
597 : {
598 50 : int32_t blocklistAll = gfxPrefs::BlocklistAll();
599 50 : if (blocklistAll > 0) {
600 0 : gfxCriticalErrorOnce(gfxCriticalError::DefaultOptions(false)) << "Forcing blocklisting all features";
601 0 : *aStatus = FEATURE_BLOCKED_DEVICE;
602 0 : aFailureId = "FEATURE_FAILURE_BLOCK_ALL";
603 0 : return NS_OK;
604 50 : } else if (blocklistAll < 0) {
605 0 : gfxCriticalErrorOnce(gfxCriticalError::DefaultOptions(false)) << "Ignoring any feature blocklisting.";
606 0 : *aStatus = FEATURE_STATUS_OK;
607 0 : return NS_OK;
608 : }
609 :
610 50 : if (GetPrefValueForFeature(aFeature, *aStatus, aFailureId)) {
611 0 : return NS_OK;
612 : }
613 :
614 50 : if (XRE_IsContentProcess()) {
615 : // Use the cached data received from the parent process.
616 2 : MOZ_ASSERT(mFeatureStatus);
617 2 : bool success = false;
618 24 : for (const auto& fs : *mFeatureStatus) {
619 24 : if (fs.feature() == aFeature) {
620 2 : aFailureId = fs.failureId();
621 2 : *aStatus = fs.status();
622 2 : success = true;
623 2 : break;
624 : }
625 : }
626 2 : return success ? NS_OK : NS_ERROR_FAILURE;
627 : }
628 :
629 96 : nsString version;
630 96 : nsTArray<GfxDriverInfo> driverInfo;
631 48 : nsresult rv = GetFeatureStatusImpl(aFeature, aStatus, version, driverInfo, aFailureId);
632 48 : return rv;
633 : }
634 :
635 : // Matching OS go somewhat beyond the simple equality check because of the
636 : // "All Windows" and "All OS X" variations.
637 : //
638 : // aBlockedOS is describing the system(s) we are trying to block.
639 : // aSystemOS is describing the system we are running on.
640 : //
641 : // aSystemOS should not be "Windows" or "OSX" - it should be set to
642 : // a particular version instead.
643 : // However, it is valid for aBlockedOS to be one of those generic values,
644 : // as we could be blocking all of the versions.
645 : inline bool
646 0 : MatchingOperatingSystems(OperatingSystem aBlockedOS, OperatingSystem aSystemOS)
647 : {
648 0 : MOZ_ASSERT(aSystemOS != OperatingSystem::Windows &&
649 : aSystemOS != OperatingSystem::OSX);
650 :
651 : // If the block entry OS is unknown, it doesn't match
652 0 : if (aBlockedOS == OperatingSystem::Unknown) {
653 0 : return false;
654 : }
655 :
656 : #if defined (XP_WIN)
657 : if (aBlockedOS == OperatingSystem::Windows) {
658 : // We do want even "unknown" aSystemOS to fall under "all windows"
659 : return true;
660 : }
661 : #endif
662 :
663 : #if defined (XP_MACOSX)
664 : if (aBlockedOS == OperatingSystem::OSX) {
665 : // We do want even "unknown" aSystemOS to fall under "all OS X"
666 : return true;
667 : }
668 : #endif
669 :
670 0 : return aSystemOS == aBlockedOS;
671 : }
672 :
673 : int32_t
674 48 : GfxInfoBase::FindBlocklistedDeviceInList(const nsTArray<GfxDriverInfo>& info,
675 : nsAString& aSuggestedVersion,
676 : int32_t aFeature,
677 : nsACString& aFailureId,
678 : OperatingSystem os)
679 : {
680 48 : int32_t status = nsIGfxInfo::FEATURE_STATUS_UNKNOWN;
681 :
682 : // Get the adapters once then reuse below
683 96 : nsAutoString adapterVendorID[2];
684 96 : nsAutoString adapterDeviceID[2];
685 96 : nsAutoString adapterDriverVersionString[2];
686 : bool adapterInfoFailed[2];
687 :
688 144 : adapterInfoFailed[0] = (NS_FAILED(GetAdapterVendorID(adapterVendorID[0])) ||
689 96 : NS_FAILED(GetAdapterDeviceID(adapterDeviceID[0])) ||
690 48 : NS_FAILED(GetAdapterDriverVersion(adapterDriverVersionString[0])));
691 96 : adapterInfoFailed[1] = (NS_FAILED(GetAdapterVendorID2(adapterVendorID[1])) ||
692 48 : NS_FAILED(GetAdapterDeviceID2(adapterDeviceID[1])) ||
693 0 : NS_FAILED(GetAdapterDriverVersion2(adapterDriverVersionString[1])));
694 : // No point in going on if we don't have adapter info
695 48 : if (adapterInfoFailed[0] && adapterInfoFailed[1]) {
696 0 : return 0;
697 : }
698 :
699 : #if defined(XP_WIN) || defined(ANDROID)
700 : uint64_t driverVersion[2] = {0, 0};
701 : if (!adapterInfoFailed[0]) {
702 : ParseDriverVersion(adapterDriverVersionString[0], &driverVersion[0]);
703 : }
704 : if (!adapterInfoFailed[1]) {
705 : ParseDriverVersion(adapterDriverVersionString[1], &driverVersion[1]);
706 : }
707 : #endif
708 :
709 48 : uint32_t i = 0;
710 48 : for (; i < info.Length(); i++) {
711 : // If we don't have the info for this GPU, no need to check further.
712 : // It is unclear that we would ever have a mixture of 1st and 2nd
713 : // GPU, but leaving the code in for that possibility for now.
714 : // (Actually, currently mGpu2 will never be true, so this can
715 : // be optimized out.)
716 0 : uint32_t infoIndex = info[i].mGpu2 ? 1 : 0;
717 0 : if (adapterInfoFailed[infoIndex]) {
718 0 : continue;
719 : }
720 :
721 : // Do the operating system check first, no point in getting the driver
722 : // info if we won't need to use it.
723 0 : if (!MatchingOperatingSystems(info[i].mOperatingSystem, os)) {
724 0 : continue;
725 : }
726 :
727 0 : if (info[i].mOperatingSystemVersion && info[i].mOperatingSystemVersion != OperatingSystemVersion()) {
728 0 : continue;
729 : }
730 :
731 0 : if (!info[i].mAdapterVendor.Equals(GfxDriverInfo::GetDeviceVendor(VendorAll), nsCaseInsensitiveStringComparator()) &&
732 0 : !info[i].mAdapterVendor.Equals(adapterVendorID[infoIndex], nsCaseInsensitiveStringComparator())) {
733 0 : continue;
734 : }
735 :
736 0 : if (info[i].mDevices != GfxDriverInfo::allDevices && info[i].mDevices->Length()) {
737 0 : bool deviceMatches = false;
738 0 : for (uint32_t j = 0; j < info[i].mDevices->Length(); j++) {
739 0 : if ((*info[i].mDevices)[j].Equals(adapterDeviceID[infoIndex], nsCaseInsensitiveStringComparator())) {
740 0 : deviceMatches = true;
741 0 : break;
742 : }
743 : }
744 :
745 0 : if (!deviceMatches) {
746 0 : continue;
747 : }
748 : }
749 :
750 0 : bool match = false;
751 :
752 0 : if (!info[i].mHardware.IsEmpty() && !info[i].mHardware.Equals(Hardware())) {
753 0 : continue;
754 : }
755 0 : if (!info[i].mModel.IsEmpty() && !info[i].mModel.Equals(Model())) {
756 0 : continue;
757 : }
758 0 : if (!info[i].mProduct.IsEmpty() && !info[i].mProduct.Equals(Product())) {
759 0 : continue;
760 : }
761 0 : if (!info[i].mManufacturer.IsEmpty() && !info[i].mManufacturer.Equals(Manufacturer())) {
762 0 : continue;
763 : }
764 :
765 : #if defined(XP_WIN) || defined(ANDROID)
766 : switch (info[i].mComparisonOp) {
767 : case DRIVER_LESS_THAN:
768 : match = driverVersion[infoIndex] < info[i].mDriverVersion;
769 : break;
770 : case DRIVER_BUILD_ID_LESS_THAN:
771 : match = (driverVersion[infoIndex] & 0xFFFF) < info[i].mDriverVersion;
772 : break;
773 : case DRIVER_LESS_THAN_OR_EQUAL:
774 : match = driverVersion[infoIndex] <= info[i].mDriverVersion;
775 : break;
776 : case DRIVER_BUILD_ID_LESS_THAN_OR_EQUAL:
777 : match = (driverVersion[infoIndex] & 0xFFFF) <= info[i].mDriverVersion;
778 : break;
779 : case DRIVER_GREATER_THAN:
780 : match = driverVersion[infoIndex] > info[i].mDriverVersion;
781 : break;
782 : case DRIVER_GREATER_THAN_OR_EQUAL:
783 : match = driverVersion[infoIndex] >= info[i].mDriverVersion;
784 : break;
785 : case DRIVER_EQUAL:
786 : match = driverVersion[infoIndex] == info[i].mDriverVersion;
787 : break;
788 : case DRIVER_NOT_EQUAL:
789 : match = driverVersion[infoIndex] != info[i].mDriverVersion;
790 : break;
791 : case DRIVER_BETWEEN_EXCLUSIVE:
792 : match = driverVersion[infoIndex] > info[i].mDriverVersion && driverVersion[infoIndex] < info[i].mDriverVersionMax;
793 : break;
794 : case DRIVER_BETWEEN_INCLUSIVE:
795 : match = driverVersion[infoIndex] >= info[i].mDriverVersion && driverVersion[infoIndex] <= info[i].mDriverVersionMax;
796 : break;
797 : case DRIVER_BETWEEN_INCLUSIVE_START:
798 : match = driverVersion[infoIndex] >= info[i].mDriverVersion && driverVersion[infoIndex] < info[i].mDriverVersionMax;
799 : break;
800 : case DRIVER_COMPARISON_IGNORED:
801 : // We don't have a comparison op, so we match everything.
802 : match = true;
803 : break;
804 : default:
805 : NS_WARNING("Bogus op in GfxDriverInfo");
806 : break;
807 : }
808 : #else
809 : // We don't care what driver version it was. We only check OS version and if
810 : // the device matches.
811 0 : match = true;
812 : #endif
813 :
814 0 : if (match || info[i].mDriverVersion == GfxDriverInfo::allDriverVersions) {
815 0 : if (info[i].mFeature == GfxDriverInfo::allFeatures ||
816 0 : info[i].mFeature == aFeature)
817 : {
818 0 : status = info[i].mFeatureStatus;
819 0 : if (!info[i].mRuleId.IsEmpty()) {
820 0 : aFailureId = info[i].mRuleId.get();
821 : } else {
822 0 : aFailureId = "FEATURE_FAILURE_DL_BLACKLIST_NO_ID";
823 : }
824 0 : break;
825 : }
826 : }
827 : }
828 :
829 : #if defined(XP_WIN)
830 : // As a very special case, we block D2D on machines with an NVidia 310M GPU
831 : // as either the primary or secondary adapter. D2D is also blocked when the
832 : // NV 310M is the primary adapter (using the standard blocklisting mechanism).
833 : // If the primary GPU already matched something in the blocklist then we
834 : // ignore this special rule. See bug 1008759.
835 : if (status == nsIGfxInfo::FEATURE_STATUS_UNKNOWN &&
836 : (aFeature == nsIGfxInfo::FEATURE_DIRECT2D)) {
837 : if (!adapterInfoFailed[1]) {
838 : nsAString &nvVendorID = (nsAString &)GfxDriverInfo::GetDeviceVendor(VendorNVIDIA);
839 : const nsString nv310mDeviceId = NS_LITERAL_STRING("0x0A70");
840 : if (nvVendorID.Equals(adapterVendorID[1], nsCaseInsensitiveStringComparator()) &&
841 : nv310mDeviceId.Equals(adapterDeviceID[1], nsCaseInsensitiveStringComparator())) {
842 : status = nsIGfxInfo::FEATURE_BLOCKED_DEVICE;
843 : aFailureId = "FEATURE_FAILURE_D2D_NV310M_BLOCK";
844 : }
845 : }
846 : }
847 :
848 : // Depends on Windows driver versioning. We don't pass a GfxDriverInfo object
849 : // back to the Windows handler, so we must handle this here.
850 : if (status == FEATURE_BLOCKED_DRIVER_VERSION) {
851 : if (info[i].mSuggestedVersion) {
852 : aSuggestedVersion.AppendPrintf("%s", info[i].mSuggestedVersion);
853 : } else if (info[i].mComparisonOp == DRIVER_LESS_THAN &&
854 : info[i].mDriverVersion != GfxDriverInfo::allDriverVersions)
855 : {
856 : aSuggestedVersion.AppendPrintf("%lld.%lld.%lld.%lld",
857 : (info[i].mDriverVersion & 0xffff000000000000) >> 48,
858 : (info[i].mDriverVersion & 0x0000ffff00000000) >> 32,
859 : (info[i].mDriverVersion & 0x00000000ffff0000) >> 16,
860 : (info[i].mDriverVersion & 0x000000000000ffff));
861 : }
862 : }
863 : #endif
864 :
865 48 : return status;
866 : }
867 :
868 : void
869 2 : GfxInfoBase::SetFeatureStatus(const nsTArray<dom::GfxInfoFeatureStatus>& aFS)
870 : {
871 2 : MOZ_ASSERT(!mFeatureStatus);
872 2 : mFeatureStatus = new nsTArray<dom::GfxInfoFeatureStatus>(aFS);
873 2 : }
874 :
875 : nsresult
876 48 : GfxInfoBase::GetFeatureStatusImpl(int32_t aFeature,
877 : int32_t* aStatus,
878 : nsAString& aSuggestedVersion,
879 : const nsTArray<GfxDriverInfo>& aDriverInfo,
880 : nsACString& aFailureId,
881 : OperatingSystem* aOS /* = nullptr */)
882 : {
883 48 : if (aFeature <= 0) {
884 0 : gfxWarning() << "Invalid feature <= 0";
885 0 : return NS_OK;
886 : }
887 :
888 48 : if (*aStatus != nsIGfxInfo::FEATURE_STATUS_UNKNOWN) {
889 : // Terminate now with the status determined by the derived type (OS-specific
890 : // code).
891 0 : return NS_OK;
892 : }
893 :
894 48 : if (mShutdownOccurred) {
895 : // This is futile; we've already commenced shutdown and our blocklists have
896 : // been deleted. We may want to look into resurrecting the blocklist instead
897 : // but for now, just don't even go there.
898 0 : return NS_OK;
899 : }
900 :
901 : // If an operating system was provided by the derived GetFeatureStatusImpl,
902 : // grab it here. Otherwise, the OS is unknown.
903 48 : OperatingSystem os = (aOS ? *aOS : OperatingSystem::Unknown);
904 :
905 96 : nsAutoString adapterVendorID;
906 96 : nsAutoString adapterDeviceID;
907 96 : nsAutoString adapterDriverVersionString;
908 144 : if (NS_FAILED(GetAdapterVendorID(adapterVendorID)) ||
909 96 : NS_FAILED(GetAdapterDeviceID(adapterDeviceID)) ||
910 48 : NS_FAILED(GetAdapterDriverVersion(adapterDriverVersionString)))
911 : {
912 0 : aFailureId = "FEATURE_FAILURE_CANT_RESOLVE_ADAPTER";
913 0 : *aStatus = FEATURE_BLOCKED_DEVICE;
914 0 : return NS_OK;
915 : }
916 :
917 : // Check if the device is blocked from the downloaded blocklist. If not, check
918 : // the static list after that. This order is used so that we can later escape
919 : // out of static blocks (i.e. if we were wrong or something was patched, we
920 : // can back out our static block without doing a release).
921 : int32_t status;
922 48 : if (aDriverInfo.Length()) {
923 0 : status = FindBlocklistedDeviceInList(aDriverInfo, aSuggestedVersion, aFeature, aFailureId, os);
924 : } else {
925 48 : if (!mDriverInfo) {
926 1 : mDriverInfo = new nsTArray<GfxDriverInfo>();
927 : }
928 48 : status = FindBlocklistedDeviceInList(GetGfxDriverInfo(), aSuggestedVersion, aFeature, aFailureId, os);
929 : }
930 :
931 : // It's now done being processed. It's safe to set the status to STATUS_OK.
932 48 : if (status == nsIGfxInfo::FEATURE_STATUS_UNKNOWN) {
933 48 : *aStatus = nsIGfxInfo::FEATURE_STATUS_OK;
934 : } else {
935 0 : *aStatus = status;
936 : }
937 :
938 48 : return NS_OK;
939 : }
940 :
941 : NS_IMETHODIMP
942 0 : GfxInfoBase::GetFeatureSuggestedDriverVersion(int32_t aFeature,
943 : nsAString& aVersion)
944 : {
945 0 : nsCString version;
946 0 : if (GetPrefValueForDriverVersion(version)) {
947 0 : aVersion = NS_ConvertASCIItoUTF16(version);
948 0 : return NS_OK;
949 : }
950 :
951 : int32_t status;
952 0 : nsCString discardFailureId;
953 0 : nsTArray<GfxDriverInfo> driverInfo;
954 0 : return GetFeatureStatusImpl(aFeature, &status, aVersion, driverInfo, discardFailureId);
955 : }
956 :
957 :
958 : NS_IMETHODIMP
959 0 : GfxInfoBase::GetWebGLParameter(const nsAString& aParam,
960 : nsAString& aResult)
961 : {
962 0 : return GfxInfoWebGL::GetWebGLParameter(aParam, aResult);
963 : }
964 :
965 : void
966 0 : GfxInfoBase::EvaluateDownloadedBlacklist(nsTArray<GfxDriverInfo>& aDriverInfo)
967 : {
968 : int32_t features[] = {
969 : nsIGfxInfo::FEATURE_DIRECT2D,
970 : nsIGfxInfo::FEATURE_DIRECT3D_9_LAYERS,
971 : nsIGfxInfo::FEATURE_DIRECT3D_10_LAYERS,
972 : nsIGfxInfo::FEATURE_DIRECT3D_10_1_LAYERS,
973 : nsIGfxInfo::FEATURE_DIRECT3D_11_LAYERS,
974 : nsIGfxInfo::FEATURE_DIRECT3D_11_ANGLE,
975 : nsIGfxInfo::FEATURE_HARDWARE_VIDEO_DECODING,
976 : nsIGfxInfo::FEATURE_OPENGL_LAYERS,
977 : nsIGfxInfo::FEATURE_WEBGL_OPENGL,
978 : nsIGfxInfo::FEATURE_WEBGL_ANGLE,
979 : nsIGfxInfo::FEATURE_WEBRTC_HW_ACCELERATION_ENCODE,
980 : nsIGfxInfo::FEATURE_WEBRTC_HW_ACCELERATION_DECODE,
981 : nsIGfxInfo::FEATURE_WEBGL_MSAA,
982 : nsIGfxInfo::FEATURE_STAGEFRIGHT,
983 : nsIGfxInfo::FEATURE_WEBRTC_HW_ACCELERATION,
984 : nsIGfxInfo::FEATURE_CANVAS2D_ACCELERATION,
985 : nsIGfxInfo::FEATURE_WEBGL2,
986 : 0
987 0 : };
988 :
989 : // For every feature we know about, we evaluate whether this blacklist has a
990 : // non-STATUS_OK status. If it does, we set the pref we evaluate in
991 : // GetFeatureStatus above, so we don't need to hold on to this blacklist
992 : // anywhere permanent.
993 0 : int i = 0;
994 0 : while (features[i]) {
995 : int32_t status;
996 0 : nsCString failureId;
997 0 : nsAutoString suggestedVersion;
998 0 : if (NS_SUCCEEDED(GetFeatureStatusImpl(features[i], &status,
999 : suggestedVersion,
1000 : aDriverInfo,
1001 : failureId))) {
1002 0 : switch (status) {
1003 : default:
1004 : case nsIGfxInfo::FEATURE_STATUS_OK:
1005 0 : RemovePrefForFeature(features[i]);
1006 0 : break;
1007 :
1008 : case nsIGfxInfo::FEATURE_BLOCKED_DRIVER_VERSION:
1009 0 : if (!suggestedVersion.IsEmpty()) {
1010 0 : SetPrefValueForDriverVersion(suggestedVersion);
1011 : } else {
1012 0 : RemovePrefForDriverVersion();
1013 : }
1014 : MOZ_FALLTHROUGH;
1015 :
1016 : case nsIGfxInfo::FEATURE_BLOCKED_MISMATCHED_VERSION:
1017 : case nsIGfxInfo::FEATURE_BLOCKED_DEVICE:
1018 : case nsIGfxInfo::FEATURE_DISCOURAGED:
1019 : case nsIGfxInfo::FEATURE_BLOCKED_OS_VERSION:
1020 0 : SetPrefValueForFeature(features[i], status, failureId);
1021 0 : break;
1022 : }
1023 : }
1024 :
1025 0 : ++i;
1026 : }
1027 0 : }
1028 :
1029 : NS_IMETHODIMP_(void)
1030 0 : GfxInfoBase::LogFailure(const nsACString &failure)
1031 : {
1032 : // gfxCriticalError has a mutex lock of its own, so we may not actually
1033 : // need this lock. ::GetFailures() accesses the data but the LogForwarder
1034 : // will not return the copy of the logs unless it can get the same lock
1035 : // that gfxCriticalError uses. Still, that is so much of an implementation
1036 : // detail that it's nicer to just add an extra lock here and in ::GetFailures()
1037 0 : MutexAutoLock lock(mMutex);
1038 :
1039 : // By default, gfxCriticalError asserts; make it not assert in this case.
1040 0 : gfxCriticalError(CriticalLog::DefaultOptions(false)) << "(LF) " << failure.BeginReading();
1041 0 : }
1042 :
1043 : /* XPConnect method of returning arrays is very ugly. Would not recommend. */
1044 0 : NS_IMETHODIMP GfxInfoBase::GetFailures(uint32_t* failureCount,
1045 : int32_t** indices,
1046 : char ***failures)
1047 : {
1048 0 : MutexAutoLock lock(mMutex);
1049 :
1050 0 : NS_ENSURE_ARG_POINTER(failureCount);
1051 0 : NS_ENSURE_ARG_POINTER(failures);
1052 :
1053 0 : *failures = nullptr;
1054 0 : *failureCount = 0;
1055 :
1056 : // indices is "allowed" to be null, the caller may not care about them,
1057 : // although calling from JS doesn't seem to get us there.
1058 0 : if (indices) *indices = nullptr;
1059 :
1060 0 : LogForwarder* logForwarder = Factory::GetLogForwarder();
1061 0 : if (!logForwarder) {
1062 0 : return NS_ERROR_UNEXPECTED;
1063 : }
1064 :
1065 : // There are two stirng copies in this method, starting with this one. We are
1066 : // assuming this is not a big deal, as the size of the array should be small
1067 : // and the strings in it should be small as well (the error messages in the
1068 : // code.) The second copy happens with the Clone() calls. Technically,
1069 : // we don't need the mutex lock after the StringVectorCopy() call.
1070 0 : LoggingRecord loggedStrings = logForwarder->LoggingRecordCopy();
1071 0 : *failureCount = loggedStrings.size();
1072 :
1073 0 : if (*failureCount != 0) {
1074 0 : *failures = (char**)moz_xmalloc(*failureCount * sizeof(char*));
1075 0 : if (!(*failures)) {
1076 0 : return NS_ERROR_OUT_OF_MEMORY;
1077 : }
1078 0 : if (indices) {
1079 0 : *indices = (int32_t*)moz_xmalloc(*failureCount * sizeof(int32_t));
1080 0 : if (!(*indices)) {
1081 0 : free(*failures);
1082 0 : *failures = nullptr;
1083 0 : return NS_ERROR_OUT_OF_MEMORY;
1084 : }
1085 : }
1086 :
1087 : /* copy over the failure messages into the array we just allocated */
1088 0 : LoggingRecord::const_iterator it;
1089 0 : uint32_t i=0;
1090 0 : for(it = loggedStrings.begin() ; it != loggedStrings.end(); ++it, i++) {
1091 0 : (*failures)[i] = (char*)nsMemory::Clone(Get<1>(*it).c_str(), Get<1>(*it).size() + 1);
1092 0 : if (indices) (*indices)[i] = Get<0>(*it);
1093 :
1094 0 : if (!(*failures)[i]) {
1095 : /* <sarcasm> I'm too afraid to use an inline function... </sarcasm> */
1096 0 : NS_FREE_XPCOM_ALLOCATED_POINTER_ARRAY(i, (*failures));
1097 0 : *failureCount = i;
1098 0 : return NS_ERROR_OUT_OF_MEMORY;
1099 : }
1100 : }
1101 : }
1102 :
1103 0 : return NS_OK;
1104 : }
1105 :
1106 : nsTArray<GfxInfoCollectorBase*> *sCollectors;
1107 :
1108 : static void
1109 9 : InitCollectors()
1110 : {
1111 9 : if (!sCollectors)
1112 3 : sCollectors = new nsTArray<GfxInfoCollectorBase*>;
1113 9 : }
1114 :
1115 0 : nsresult GfxInfoBase::GetInfo(JSContext* aCx, JS::MutableHandle<JS::Value> aResult)
1116 : {
1117 0 : InitCollectors();
1118 0 : InfoObject obj(aCx);
1119 :
1120 0 : for (uint32_t i = 0; i < sCollectors->Length(); i++) {
1121 0 : (*sCollectors)[i]->GetInfo(obj);
1122 : }
1123 :
1124 : // Some example property definitions
1125 : // obj.DefineProperty("wordCacheSize", gfxTextRunWordCache::Count());
1126 : // obj.DefineProperty("renderer", mRendererIDsString);
1127 : // obj.DefineProperty("five", 5);
1128 :
1129 0 : if (!obj.mOk) {
1130 0 : return NS_ERROR_FAILURE;
1131 : }
1132 :
1133 0 : aResult.setObject(*obj.mObj);
1134 0 : return NS_OK;
1135 : }
1136 :
1137 3 : nsAutoCString gBaseAppVersion;
1138 :
1139 : const nsCString&
1140 0 : GfxInfoBase::GetApplicationVersion()
1141 : {
1142 : static bool versionInitialized = false;
1143 0 : if (!versionInitialized) {
1144 : // If we fail to get the version, we will not try again.
1145 0 : versionInitialized = true;
1146 :
1147 : // Get the version from xpcom/system/nsIXULAppInfo.idl
1148 0 : nsCOMPtr<nsIXULAppInfo> app = do_GetService("@mozilla.org/xre/app-info;1");
1149 0 : if (app) {
1150 0 : app->GetVersion(gBaseAppVersion);
1151 : }
1152 : }
1153 0 : return gBaseAppVersion;
1154 : }
1155 :
1156 : void
1157 9 : GfxInfoBase::AddCollector(GfxInfoCollectorBase* collector)
1158 : {
1159 9 : InitCollectors();
1160 9 : sCollectors->AppendElement(collector);
1161 9 : }
1162 :
1163 : void
1164 0 : GfxInfoBase::RemoveCollector(GfxInfoCollectorBase* collector)
1165 : {
1166 0 : InitCollectors();
1167 0 : for (uint32_t i = 0; i < sCollectors->Length(); i++) {
1168 0 : if ((*sCollectors)[i] == collector) {
1169 0 : sCollectors->RemoveElementAt(i);
1170 0 : break;
1171 : }
1172 : }
1173 0 : if (sCollectors->IsEmpty()) {
1174 0 : delete sCollectors;
1175 0 : sCollectors = nullptr;
1176 : }
1177 0 : }
1178 :
1179 : NS_IMETHODIMP
1180 0 : GfxInfoBase::GetMonitors(JSContext* aCx, JS::MutableHandleValue aResult)
1181 : {
1182 0 : JS::Rooted<JSObject*> array(aCx, JS_NewArrayObject(aCx, 0));
1183 :
1184 0 : nsresult rv = FindMonitors(aCx, array);
1185 0 : if (NS_FAILED(rv)) {
1186 0 : return rv;
1187 : }
1188 :
1189 0 : aResult.setObject(*array);
1190 0 : return NS_OK;
1191 : }
1192 :
1193 : static const char*
1194 2 : GetLayersBackendName(layers::LayersBackend aBackend)
1195 : {
1196 2 : switch (aBackend) {
1197 : case layers::LayersBackend::LAYERS_NONE:
1198 1 : return "none";
1199 : case layers::LayersBackend::LAYERS_OPENGL:
1200 0 : return "opengl";
1201 : case layers::LayersBackend::LAYERS_D3D11:
1202 0 : return "d3d11";
1203 : case layers::LayersBackend::LAYERS_CLIENT:
1204 0 : return "client";
1205 : case layers::LayersBackend::LAYERS_WR:
1206 0 : return "webrender";
1207 : case layers::LayersBackend::LAYERS_BASIC:
1208 1 : return "basic";
1209 : default:
1210 0 : MOZ_ASSERT_UNREACHABLE("unknown layers backend");
1211 : return "unknown";
1212 : }
1213 : }
1214 :
1215 : static inline bool
1216 2 : SetJSPropertyString(JSContext* aCx, JS::Handle<JSObject*> aObj,
1217 : const char* aProp, const char* aString)
1218 : {
1219 4 : JS::Rooted<JSString*> str(aCx, JS_NewStringCopyZ(aCx, aString));
1220 2 : if (!str) {
1221 0 : return false;
1222 : }
1223 :
1224 4 : JS::Rooted<JS::Value> val(aCx, JS::StringValue(str));
1225 2 : return JS_SetProperty(aCx, aObj, aProp, val);
1226 : }
1227 :
1228 : template <typename T>
1229 : static inline bool
1230 0 : AppendJSElement(JSContext* aCx, JS::Handle<JSObject*> aObj, const T& aValue)
1231 : {
1232 : uint32_t index;
1233 0 : if (!JS_GetArrayLength(aCx, aObj, &index)) {
1234 0 : return false;
1235 : }
1236 0 : return JS_SetElement(aCx, aObj, index, aValue);
1237 : }
1238 :
1239 : nsresult
1240 2 : GfxInfoBase::GetFeatures(JSContext* aCx, JS::MutableHandle<JS::Value> aOut)
1241 : {
1242 4 : JS::Rooted<JSObject*> obj(aCx, JS_NewPlainObject(aCx));
1243 2 : if (!obj) {
1244 0 : return NS_ERROR_OUT_OF_MEMORY;
1245 : }
1246 2 : aOut.setObject(*obj);
1247 :
1248 2 : layers::LayersBackend backend = gfxPlatform::Initialized()
1249 2 : ? gfxPlatform::GetPlatform()->GetCompositorBackend()
1250 2 : : layers::LayersBackend::LAYERS_NONE;
1251 2 : const char* backendName = GetLayersBackendName(backend);
1252 2 : SetJSPropertyString(aCx, obj, "compositor", backendName);
1253 :
1254 : // If graphics isn't initialized yet, just stop now.
1255 2 : if (!gfxPlatform::Initialized()) {
1256 0 : return NS_OK;
1257 : }
1258 :
1259 2 : DescribeFeatures(aCx, obj);
1260 2 : return NS_OK;
1261 : }
1262 :
1263 0 : nsresult GfxInfoBase::GetFeatureLog(JSContext* aCx, JS::MutableHandle<JS::Value> aOut)
1264 : {
1265 0 : JS::Rooted<JSObject*> containerObj(aCx, JS_NewPlainObject(aCx));
1266 0 : if (!containerObj) {
1267 0 : return NS_ERROR_OUT_OF_MEMORY;
1268 : }
1269 0 : aOut.setObject(*containerObj);
1270 :
1271 0 : JS::Rooted<JSObject*> featureArray(aCx, JS_NewArrayObject(aCx, 0));
1272 0 : if (!featureArray) {
1273 0 : return NS_ERROR_OUT_OF_MEMORY;
1274 : }
1275 :
1276 : // Collect features.
1277 0 : gfxConfig::ForEachFeature([&](const char* aName,
1278 : const char* aDescription,
1279 0 : FeatureState& aFeature) -> void {
1280 0 : JS::Rooted<JSObject*> obj(aCx, JS_NewPlainObject(aCx));
1281 0 : if (!obj) {
1282 0 : return;
1283 : }
1284 0 : if (!SetJSPropertyString(aCx, obj, "name", aName) ||
1285 0 : !SetJSPropertyString(aCx, obj, "description", aDescription) ||
1286 0 : !SetJSPropertyString(aCx, obj, "status", FeatureStatusToString(aFeature.GetValue())))
1287 : {
1288 0 : return;
1289 : }
1290 :
1291 0 : JS::Rooted<JS::Value> log(aCx);
1292 0 : if (!BuildFeatureStateLog(aCx, aFeature, &log)) {
1293 0 : return;
1294 : }
1295 0 : if (!JS_SetProperty(aCx, obj, "log", log)) {
1296 0 : return;
1297 : }
1298 :
1299 0 : if (!AppendJSElement(aCx, featureArray, obj)) {
1300 0 : return;
1301 : }
1302 0 : });
1303 :
1304 0 : JS::Rooted<JSObject*> fallbackArray(aCx, JS_NewArrayObject(aCx, 0));
1305 0 : if (!fallbackArray) {
1306 0 : return NS_ERROR_OUT_OF_MEMORY;
1307 : }
1308 :
1309 : // Collect fallbacks.
1310 0 : gfxConfig::ForEachFallback([&](const char* aName, const char* aMessage) -> void {
1311 0 : JS::Rooted<JSObject*> obj(aCx, JS_NewPlainObject(aCx));
1312 0 : if (!obj) {
1313 0 : return;
1314 : }
1315 :
1316 0 : if (!SetJSPropertyString(aCx, obj, "name", aName) ||
1317 0 : !SetJSPropertyString(aCx, obj, "message", aMessage))
1318 : {
1319 0 : return;
1320 : }
1321 :
1322 0 : if (!AppendJSElement(aCx, fallbackArray, obj)) {
1323 0 : return;
1324 : }
1325 0 : });
1326 :
1327 0 : JS::Rooted<JS::Value> val(aCx);
1328 :
1329 0 : val = JS::ObjectValue(*featureArray);
1330 0 : JS_SetProperty(aCx, containerObj, "features", val);
1331 :
1332 0 : val = JS::ObjectValue(*fallbackArray);
1333 0 : JS_SetProperty(aCx, containerObj, "fallbacks", val);
1334 :
1335 0 : return NS_OK;
1336 : }
1337 :
1338 : bool
1339 0 : GfxInfoBase::BuildFeatureStateLog(JSContext* aCx, const FeatureState& aFeature,
1340 : JS::MutableHandle<JS::Value> aOut)
1341 : {
1342 0 : JS::Rooted<JSObject*> log(aCx, JS_NewArrayObject(aCx, 0));
1343 0 : if (!log) {
1344 0 : return false;
1345 : }
1346 0 : aOut.setObject(*log);
1347 :
1348 0 : aFeature.ForEachStatusChange([&](const char* aType,
1349 : FeatureStatus aStatus,
1350 0 : const char* aMessage) -> void {
1351 0 : JS::Rooted<JSObject*> obj(aCx, JS_NewPlainObject(aCx));
1352 0 : if (!obj) {
1353 0 : return;
1354 : }
1355 :
1356 0 : if (!SetJSPropertyString(aCx, obj, "type", aType) ||
1357 0 : !SetJSPropertyString(aCx, obj, "status", FeatureStatusToString(aStatus)) ||
1358 0 : (aMessage && !SetJSPropertyString(aCx, obj, "message", aMessage)))
1359 : {
1360 0 : return;
1361 : }
1362 :
1363 0 : if (!AppendJSElement(aCx, log, obj)) {
1364 0 : return;
1365 : }
1366 0 : });
1367 :
1368 0 : return true;
1369 : }
1370 :
1371 : void
1372 2 : GfxInfoBase::DescribeFeatures(JSContext* aCx, JS::Handle<JSObject*> aObj)
1373 : {
1374 4 : JS::Rooted<JSObject*> obj(aCx);
1375 :
1376 2 : gfx::FeatureStatus gpuProcess = gfxConfig::GetValue(Feature::GPU_PROCESS);
1377 2 : InitFeatureObject(aCx, aObj, "gpuProcess", FEATURE_GPU_PROCESS, Some(gpuProcess), &obj);
1378 :
1379 : // Only include AL if the platform attempted to use it.
1380 2 : gfx::FeatureStatus advancedLayers = gfxConfig::GetValue(Feature::ADVANCED_LAYERS);
1381 2 : if (advancedLayers != FeatureStatus::Unused) {
1382 0 : InitFeatureObject(aCx, aObj, "advancedLayers", FEATURE_ADVANCED_LAYERS,
1383 0 : Some(advancedLayers), &obj);
1384 :
1385 0 : if (gfxConfig::UseFallback(Fallback::NO_CONSTANT_BUFFER_OFFSETTING)) {
1386 0 : JS::Rooted<JS::Value> trueVal(aCx, JS::BooleanValue(true));
1387 0 : JS_SetProperty(aCx, obj, "noConstantBufferOffsetting", trueVal);
1388 : }
1389 : }
1390 2 : }
1391 :
1392 : bool
1393 2 : GfxInfoBase::InitFeatureObject(JSContext* aCx,
1394 : JS::Handle<JSObject*> aContainer,
1395 : const char* aName,
1396 : int32_t aFeature,
1397 : const Maybe<mozilla::gfx::FeatureStatus>& aFeatureStatus,
1398 : JS::MutableHandle<JSObject*> aOutObj)
1399 : {
1400 4 : JS::Rooted<JSObject*> obj(aCx, JS_NewPlainObject(aCx));
1401 2 : if (!obj) {
1402 0 : return false;
1403 : }
1404 :
1405 4 : nsCString failureId = NS_LITERAL_CSTRING("OK");
1406 : int32_t unused;
1407 2 : if (!NS_SUCCEEDED(GetFeatureStatus(aFeature, failureId, &unused))) {
1408 0 : return false;
1409 : }
1410 :
1411 : // Set "status".
1412 2 : if (aFeatureStatus) {
1413 2 : const char* status = FeatureStatusToString(aFeatureStatus.value());
1414 :
1415 4 : JS::Rooted<JSString*> str(aCx, JS_NewStringCopyZ(aCx, status));
1416 4 : JS::Rooted<JS::Value> val(aCx, JS::StringValue(str));
1417 2 : JS_SetProperty(aCx, obj, "status", val);
1418 : }
1419 :
1420 : // Add the feature object to the container.
1421 : {
1422 4 : JS::Rooted<JS::Value> val(aCx, JS::ObjectValue(*obj));
1423 2 : JS_SetProperty(aCx, aContainer, aName, val);
1424 : }
1425 :
1426 2 : aOutObj.set(obj);
1427 2 : return true;
1428 : }
1429 :
1430 : nsresult
1431 0 : GfxInfoBase::GetActiveCrashGuards(JSContext* aCx, JS::MutableHandle<JS::Value> aOut)
1432 : {
1433 0 : JS::Rooted<JSObject*> array(aCx, JS_NewArrayObject(aCx, 0));
1434 0 : if (!array) {
1435 0 : return NS_ERROR_OUT_OF_MEMORY;
1436 : }
1437 0 : aOut.setObject(*array);
1438 :
1439 0 : DriverCrashGuard::ForEachActiveCrashGuard([&](const char* aName,
1440 0 : const char* aPrefName) -> void {
1441 0 : JS::Rooted<JSObject*> obj(aCx, JS_NewPlainObject(aCx));
1442 0 : if (!obj) {
1443 0 : return;
1444 : }
1445 0 : if (!SetJSPropertyString(aCx, obj, "type", aName)) {
1446 0 : return;
1447 : }
1448 0 : if (!SetJSPropertyString(aCx, obj, "prefName", aPrefName)) {
1449 0 : return;
1450 : }
1451 0 : if (!AppendJSElement(aCx, array, obj)) {
1452 0 : return;
1453 : }
1454 0 : });
1455 :
1456 0 : return NS_OK;
1457 : }
1458 :
1459 : NS_IMETHODIMP
1460 0 : GfxInfoBase::GetWebRenderEnabled(bool* aWebRenderEnabled)
1461 : {
1462 0 : *aWebRenderEnabled = gfxVars::UseWebRender();
1463 0 : return NS_OK;
1464 : }
1465 :
1466 : NS_IMETHODIMP
1467 1 : GfxInfoBase::GetContentBackend(nsAString & aContentBackend)
1468 : {
1469 1 : BackendType backend = gfxPlatform::GetPlatform()->GetDefaultContentBackend();
1470 2 : nsString outStr;
1471 :
1472 1 : switch (backend) {
1473 : case BackendType::DIRECT2D1_1: {
1474 0 : outStr.AppendPrintf("Direct2D 1.1");
1475 0 : break;
1476 : }
1477 : case BackendType::SKIA: {
1478 1 : outStr.AppendPrintf("Skia");
1479 1 : break;
1480 : }
1481 : case BackendType::CAIRO: {
1482 0 : outStr.AppendPrintf("Cairo");
1483 0 : break;
1484 : }
1485 : default:
1486 0 : return NS_ERROR_FAILURE;
1487 : }
1488 :
1489 1 : aContentBackend.Assign(outStr);
1490 1 : return NS_OK;
1491 : }
1492 :
1493 : NS_IMETHODIMP
1494 0 : GfxInfoBase::GetUsingGPUProcess(bool *aOutValue)
1495 : {
1496 0 : GPUProcessManager* gpu = GPUProcessManager::Get();
1497 0 : if (!gpu) {
1498 : // Not supported in content processes.
1499 0 : return NS_ERROR_FAILURE;
1500 : }
1501 :
1502 0 : *aOutValue = !!gpu->GetGPUChild();
1503 0 : return NS_OK;
1504 : }
1505 :
1506 : NS_IMETHODIMP
1507 0 : GfxInfoBase::ControlGPUProcessForXPCShell(bool aEnable, bool *_retval)
1508 : {
1509 0 : gfxPlatform::GetPlatform();
1510 :
1511 0 : GPUProcessManager* gpm = GPUProcessManager::Get();
1512 0 : if (aEnable) {
1513 0 : if (!gfxConfig::IsEnabled(Feature::GPU_PROCESS)) {
1514 0 : gfxConfig::UserForceEnable(Feature::GPU_PROCESS, "xpcshell-test");
1515 : }
1516 0 : gpm->LaunchGPUProcess();
1517 0 : gpm->EnsureGPUReady();
1518 : } else {
1519 0 : gpm->KillProcess();
1520 : }
1521 :
1522 0 : *_retval = true;
1523 0 : return NS_OK;
1524 : }
1525 :
1526 9 : GfxInfoCollectorBase::GfxInfoCollectorBase()
1527 : {
1528 9 : GfxInfoBase::AddCollector(this);
1529 9 : }
1530 :
1531 0 : GfxInfoCollectorBase::~GfxInfoCollectorBase()
1532 : {
1533 0 : GfxInfoBase::RemoveCollector(this);
1534 0 : }
|