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 : * ***** BEGIN LICENSE BLOCK *****
5 : * Version: MPL 1.1/GPL 2.0/LGPL 2.1
6 : *
7 : * The contents of this file are subject to the Mozilla Public License Version
8 : * 1.1 (the "License"); you may not use this file except in compliance with
9 : * the License. You may obtain a copy of the License at
10 : * http://www.mozilla.org/MPL/
11 : *
12 : * Software distributed under the License is distributed on an "AS IS" basis,
13 : * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
14 : * for the specific language governing rights and limitations under the
15 : * License.
16 : *
17 : * The Original Code is mozilla.org code.
18 : *
19 : * The Initial Developer of the Original Code is
20 : * Mozilla Foundation.
21 : * Portions created by the Initial Developer are Copyright (C) 2011
22 : * the Initial Developer. All Rights Reserved.
23 : *
24 : * Contributor(s):
25 : *
26 : * Alternatively, the contents of this file may be used under the terms of
27 : * either the GNU General Public License Version 2 or later (the "GPL"), or
28 : * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
29 : * in which case the provisions of the GPL or the LGPL are applicable instead
30 : * of those above. If you wish to allow use of your version of this file only
31 : * under the terms of either the GPL or the LGPL, and not to allow others to
32 : * use your version of this file under the terms of the MPL, indicate your
33 : * decision by deleting the provisions above and replace them with the notice
34 : * and other provisions required by the GPL or the LGPL. If you do not delete
35 : * the provisions above, a recipient may use your version of this file under
36 : * the terms of any one of the MPL, the GPL or the LGPL.
37 : *
38 : * ***** END LICENSE BLOCK ***** */
39 :
40 : #include "mozilla/Util.h"
41 :
42 : #include "GfxInfoBase.h"
43 :
44 : #include "GfxInfoWebGL.h"
45 : #include "GfxDriverInfo.h"
46 : #include "nsCOMPtr.h"
47 : #include "nsCOMArray.h"
48 : #include "nsAutoPtr.h"
49 : #include "nsString.h"
50 : #include "nsUnicharUtils.h"
51 : #include "mozilla/Services.h"
52 : #include "mozilla/Observer.h"
53 : #include "nsIObserver.h"
54 : #include "nsIObserverService.h"
55 : #include "nsIDOMElement.h"
56 : #include "nsIDOMNode.h"
57 : #include "nsIDOMNodeList.h"
58 : #include "nsTArray.h"
59 : #include "mozilla/Preferences.h"
60 :
61 : #if defined(MOZ_CRASHREPORTER)
62 : #include "nsExceptionHandler.h"
63 : #endif
64 :
65 : using namespace mozilla::widget;
66 : using namespace mozilla;
67 :
68 : nsTArray<GfxDriverInfo>* GfxInfoBase::mDriverInfo;
69 : bool GfxInfoBase::mDriverInfoObserverInitialized;
70 :
71 : // Observes for shutdown so that the child GfxDriverInfo list is freed.
72 : class ShutdownObserver : public nsIObserver
73 : {
74 : public:
75 26 : ShutdownObserver() {}
76 104 : virtual ~ShutdownObserver() {}
77 :
78 : NS_DECL_ISUPPORTS
79 :
80 26 : NS_IMETHOD Observe(nsISupports *subject, const char *aTopic,
81 : const PRUnichar *aData)
82 : {
83 26 : MOZ_ASSERT(strcmp(aTopic, NS_XPCOM_SHUTDOWN_OBSERVER_ID) == 0);
84 :
85 26 : delete GfxInfoBase::mDriverInfo;
86 26 : GfxInfoBase::mDriverInfo = nsnull;
87 :
88 260 : for (PRUint32 i = 0; i < DeviceFamilyMax; i++)
89 234 : delete GfxDriverInfo::mDeviceFamilies[i];
90 :
91 156 : for (PRUint32 i = 0; i < DeviceVendorMax; i++)
92 130 : delete GfxDriverInfo::mDeviceVendors[i];
93 :
94 26 : return NS_OK;
95 : }
96 : };
97 :
98 156 : NS_IMPL_ISUPPORTS1(ShutdownObserver, nsIObserver);
99 :
100 26 : void InitGfxDriverInfoShutdownObserver()
101 : {
102 26 : if (GfxInfoBase::mDriverInfoObserverInitialized)
103 0 : return;
104 :
105 26 : GfxInfoBase::mDriverInfoObserverInitialized = true;
106 :
107 52 : nsCOMPtr<nsIObserverService> observerService = services::GetObserverService();
108 26 : if (!observerService) {
109 0 : NS_WARNING("Could not get observer service!");
110 : return;
111 : }
112 :
113 26 : ShutdownObserver *obs = new ShutdownObserver();
114 26 : observerService->AddObserver(obs, NS_XPCOM_SHUTDOWN_OBSERVER_ID, false);
115 : }
116 :
117 : extern "C" {
118 : void StoreSpline(int ax, int ay, int bx, int by, int cx, int cy, int dx, int dy);
119 : void CrashSpline(double tolerance, int ax, int ay, int bx, int by, int cx, int cy, int dx, int dy);
120 : }
121 :
122 : static int crash_ax;
123 : static int crash_ay;
124 : static int crash_bx;
125 : static int crash_by;
126 : static int crash_cx;
127 : static int crash_cy;
128 : static int crash_dx;
129 : static int crash_dy;
130 :
131 : void
132 0 : StoreSpline(int ax, int ay, int bx, int by, int cx, int cy, int dx, int dy) {
133 0 : crash_ax = ax;
134 0 : crash_ay = ay;
135 0 : crash_bx = bx;
136 0 : crash_by = by;
137 0 : crash_cx = cx;
138 0 : crash_cy = cy;
139 0 : crash_dx = dx;
140 0 : crash_dy = dy;
141 0 : }
142 :
143 : void
144 0 : CrashSpline(double tolerance, int ax, int ay, int bx, int by, int cx, int cy, int dx, int dy) {
145 : #if defined(MOZ_CRASHREPORTER)
146 : static bool annotated;
147 :
148 0 : if (!annotated) {
149 0 : nsCAutoString note;
150 :
151 0 : note.AppendPrintf("curve ");
152 0 : note.AppendPrintf("%x ", crash_ax);
153 0 : note.AppendPrintf("%x, ", crash_ay);
154 0 : note.AppendPrintf("%x ", crash_bx);
155 0 : note.AppendPrintf("%x, ", crash_by);
156 0 : note.AppendPrintf("%x ", crash_cx);
157 0 : note.AppendPrintf("%x, ", crash_cy);
158 0 : note.AppendPrintf("%x ", crash_dx);
159 0 : note.AppendPrintf("%x\n", crash_dy);
160 0 : note.AppendPrintf("crv-crash(%f): ", tolerance);
161 0 : note.AppendPrintf("%x ", ax);
162 0 : note.AppendPrintf("%x, ", ay);
163 0 : note.AppendPrintf("%x ", bx);
164 0 : note.AppendPrintf("%x, ", by);
165 0 : note.AppendPrintf("%x ", cx);
166 0 : note.AppendPrintf("%x, ", cy);
167 0 : note.AppendPrintf("%x ", dx);
168 0 : note.AppendPrintf("%x\n", dy);
169 :
170 0 : CrashReporter::AppendAppNotesToCrashReport(note);
171 0 : annotated = true;
172 : }
173 : #endif
174 0 : }
175 :
176 :
177 : using namespace mozilla::widget;
178 : using namespace mozilla;
179 :
180 1059 : NS_IMPL_ISUPPORTS3(GfxInfoBase, nsIGfxInfo, nsIObserver, nsISupportsWeakReference)
181 :
182 : #define BLACKLIST_PREF_BRANCH "gfx.blacklist."
183 : #define SUGGESTED_VERSION_PREF BLACKLIST_PREF_BRANCH "suggested-driver-version"
184 : #define BLACKLIST_ENTRY_TAG_NAME "gfxBlacklistEntry"
185 :
186 : static const char*
187 100 : GetPrefNameForFeature(PRInt32 aFeature)
188 : {
189 100 : const char* name = nsnull;
190 100 : switch(aFeature) {
191 : case nsIGfxInfo::FEATURE_DIRECT2D:
192 20 : name = BLACKLIST_PREF_BRANCH "direct2d";
193 20 : break;
194 : case nsIGfxInfo::FEATURE_DIRECT3D_9_LAYERS:
195 20 : name = BLACKLIST_PREF_BRANCH "layers.direct3d9";
196 20 : break;
197 : case nsIGfxInfo::FEATURE_DIRECT3D_10_LAYERS:
198 10 : name = BLACKLIST_PREF_BRANCH "layers.direct3d10";
199 10 : break;
200 : case nsIGfxInfo::FEATURE_DIRECT3D_10_1_LAYERS:
201 10 : name = BLACKLIST_PREF_BRANCH "layers.direct3d10-1";
202 10 : break;
203 : case nsIGfxInfo::FEATURE_OPENGL_LAYERS:
204 10 : name = BLACKLIST_PREF_BRANCH "layers.opengl";
205 10 : break;
206 : case nsIGfxInfo::FEATURE_WEBGL_OPENGL:
207 10 : name = BLACKLIST_PREF_BRANCH "webgl.opengl";
208 10 : break;
209 : case nsIGfxInfo::FEATURE_WEBGL_ANGLE:
210 10 : name = BLACKLIST_PREF_BRANCH "webgl.angle";
211 10 : break;
212 : case nsIGfxInfo::FEATURE_WEBGL_MSAA:
213 10 : name = BLACKLIST_PREF_BRANCH "webgl.msaa";
214 10 : break;
215 : default:
216 0 : break;
217 : };
218 :
219 100 : return name;
220 : }
221 :
222 : // Returns the value of the pref for the relevant feature in aValue.
223 : // If the pref doesn't exist, aValue is not touched, and returns false.
224 : static bool
225 20 : GetPrefValueForFeature(PRInt32 aFeature, PRInt32& aValue)
226 : {
227 20 : const char *prefname = GetPrefNameForFeature(aFeature);
228 20 : if (!prefname)
229 0 : return false;
230 :
231 20 : aValue = false;
232 20 : return NS_SUCCEEDED(Preferences::GetInt(prefname, &aValue));
233 : }
234 :
235 : static void
236 4 : SetPrefValueForFeature(PRInt32 aFeature, PRInt32 aValue)
237 : {
238 4 : const char *prefname = GetPrefNameForFeature(aFeature);
239 4 : if (!prefname)
240 0 : return;
241 :
242 4 : Preferences::SetInt(prefname, aValue);
243 : }
244 :
245 : static void
246 76 : RemovePrefForFeature(PRInt32 aFeature)
247 : {
248 76 : const char *prefname = GetPrefNameForFeature(aFeature);
249 76 : if (!prefname)
250 0 : return;
251 :
252 76 : Preferences::ClearUser(prefname);
253 : }
254 :
255 : static bool
256 0 : GetPrefValueForDriverVersion(nsCString& aVersion)
257 : {
258 0 : return NS_SUCCEEDED(Preferences::GetCString(SUGGESTED_VERSION_PREF,
259 : &aVersion));
260 : }
261 :
262 : static void
263 0 : SetPrefValueForDriverVersion(const nsAString& aVersion)
264 : {
265 0 : Preferences::SetString(SUGGESTED_VERSION_PREF, aVersion);
266 0 : }
267 :
268 : static void
269 4 : RemovePrefForDriverVersion()
270 : {
271 4 : Preferences::ClearUser(SUGGESTED_VERSION_PREF);
272 4 : }
273 :
274 : // <foo>Hello</foo> - "Hello" is stored as a child text node of the foo node.
275 : static bool
276 642 : BlacklistNodeToTextValue(nsIDOMNode *aBlacklistNode, nsAString& aValue)
277 : {
278 1284 : nsAutoString value;
279 642 : if (NS_FAILED(aBlacklistNode->GetTextContent(value)))
280 0 : return false;
281 :
282 642 : value.Trim(" \t\r\n");
283 642 : aValue = value;
284 :
285 642 : return true;
286 : }
287 :
288 : static OperatingSystem
289 76 : BlacklistOSToOperatingSystem(const nsAString& os)
290 : {
291 76 : if (os == NS_LITERAL_STRING("WINNT 5.1"))
292 0 : return DRIVER_OS_WINDOWS_XP;
293 76 : else if (os == NS_LITERAL_STRING("WINNT 5.2"))
294 0 : return DRIVER_OS_WINDOWS_SERVER_2003;
295 76 : else if (os == NS_LITERAL_STRING("WINNT 6.0"))
296 10 : return DRIVER_OS_WINDOWS_VISTA;
297 66 : else if (os == NS_LITERAL_STRING("WINNT 6.1"))
298 26 : return DRIVER_OS_WINDOWS_7;
299 40 : else if (os == NS_LITERAL_STRING("Linux"))
300 8 : return DRIVER_OS_LINUX;
301 32 : else if (os == NS_LITERAL_STRING("Darwin 9"))
302 8 : return DRIVER_OS_OS_X_10_5;
303 24 : else if (os == NS_LITERAL_STRING("Darwin 10"))
304 0 : return DRIVER_OS_OS_X_10_6;
305 24 : else if (os == NS_LITERAL_STRING("Darwin 11"))
306 0 : return DRIVER_OS_OS_X_10_7;
307 24 : else if (os == NS_LITERAL_STRING("Android"))
308 24 : return DRIVER_OS_ANDROID;
309 0 : else if (os == NS_LITERAL_STRING("All"))
310 0 : return DRIVER_OS_ALL;
311 :
312 0 : return DRIVER_OS_UNKNOWN;
313 : }
314 :
315 : static GfxDeviceFamily*
316 76 : BlacklistDevicesToDeviceFamily(nsIDOMNodeList* aDevices)
317 : {
318 : PRUint32 length;
319 76 : if (NS_FAILED(aDevices->GetLength(&length)))
320 0 : return nsnull;
321 :
322 : // For each <device>, get its device ID, and return a freshly-allocated
323 : // GfxDeviceFamily with the contents of that array.
324 76 : GfxDeviceFamily* deviceIds = new GfxDeviceFamily;
325 :
326 294 : for (PRUint32 i = 0; i < length; ++i) {
327 436 : nsCOMPtr<nsIDOMNode> node;
328 218 : if (NS_FAILED(aDevices->Item(i, getter_AddRefs(node))) || !node)
329 0 : continue;
330 :
331 436 : nsAutoString deviceValue;
332 218 : if (!BlacklistNodeToTextValue(node, deviceValue))
333 0 : continue;
334 :
335 436 : deviceIds->AppendElement(deviceValue);
336 : }
337 :
338 76 : return deviceIds;
339 : }
340 :
341 : static PRInt32
342 76 : BlacklistFeatureToGfxFeature(const nsAString& aFeature)
343 : {
344 76 : if (aFeature == NS_LITERAL_STRING("DIRECT2D"))
345 66 : return nsIGfxInfo::FEATURE_DIRECT2D;
346 10 : else if (aFeature == NS_LITERAL_STRING("DIRECT3D_9_LAYERS"))
347 10 : return nsIGfxInfo::FEATURE_DIRECT3D_9_LAYERS;
348 0 : else if (aFeature == NS_LITERAL_STRING("DIRECT3D_10_LAYERS"))
349 0 : return nsIGfxInfo::FEATURE_DIRECT3D_10_LAYERS;
350 0 : else if (aFeature == NS_LITERAL_STRING("DIRECT3D_10_1_LAYERS"))
351 0 : return nsIGfxInfo::FEATURE_DIRECT3D_10_1_LAYERS;
352 0 : else if (aFeature == NS_LITERAL_STRING("OPENGL_LAYERS"))
353 0 : return nsIGfxInfo::FEATURE_OPENGL_LAYERS;
354 0 : else if (aFeature == NS_LITERAL_STRING("WEBGL_OPENGL"))
355 0 : return nsIGfxInfo::FEATURE_WEBGL_OPENGL;
356 0 : else if (aFeature == NS_LITERAL_STRING("WEBGL_ANGLE"))
357 0 : return nsIGfxInfo::FEATURE_WEBGL_ANGLE;
358 0 : else if (aFeature == NS_LITERAL_STRING("WEBGL_MSAA"))
359 0 : return nsIGfxInfo::FEATURE_WEBGL_MSAA;
360 :
361 0 : return 0;
362 : }
363 :
364 : static PRInt32
365 76 : BlacklistFeatureStatusToGfxFeatureStatus(const nsAString& aStatus)
366 : {
367 76 : if (aStatus == NS_LITERAL_STRING("NO_INFO"))
368 0 : return nsIGfxInfo::FEATURE_NO_INFO;
369 76 : else if (aStatus == NS_LITERAL_STRING("BLOCKED_DRIVER_VERSION"))
370 76 : return nsIGfxInfo::FEATURE_BLOCKED_DRIVER_VERSION;
371 0 : else if (aStatus == NS_LITERAL_STRING("BLOCKED_DEVICE"))
372 0 : return nsIGfxInfo::FEATURE_BLOCKED_DEVICE;
373 0 : else if (aStatus == NS_LITERAL_STRING("DISCOURAGED"))
374 0 : return nsIGfxInfo::FEATURE_DISCOURAGED;
375 0 : else if (aStatus == NS_LITERAL_STRING("BLOCKED_OS_VERSION"))
376 0 : return nsIGfxInfo::FEATURE_BLOCKED_OS_VERSION;
377 :
378 : // Do not allow it to set STATUS_UNKNOWN.
379 :
380 0 : return nsIGfxInfo::FEATURE_NO_INFO;
381 : }
382 :
383 : static VersionComparisonOp
384 60 : BlacklistComparatorToComparisonOp(const nsAString& op)
385 : {
386 60 : if (op == NS_LITERAL_STRING("LESS_THAN"))
387 20 : return DRIVER_LESS_THAN;
388 40 : else if (op == NS_LITERAL_STRING("LESS_THAN_OR_EQUAL"))
389 8 : return DRIVER_LESS_THAN_OR_EQUAL;
390 32 : else if (op == NS_LITERAL_STRING("GREATER_THAN"))
391 0 : return DRIVER_GREATER_THAN;
392 32 : else if (op == NS_LITERAL_STRING("GREATER_THAN_OR_EQUAL"))
393 16 : return DRIVER_GREATER_THAN_OR_EQUAL;
394 16 : else if (op == NS_LITERAL_STRING("EQUAL"))
395 16 : return DRIVER_EQUAL;
396 0 : else if (op == NS_LITERAL_STRING("NOT_EQUAL"))
397 0 : return DRIVER_NOT_EQUAL;
398 0 : else if (op == NS_LITERAL_STRING("BETWEEN_EXCLUSIVE"))
399 0 : return DRIVER_BETWEEN_EXCLUSIVE;
400 0 : else if (op == NS_LITERAL_STRING("BETWEEN_INCLUSIVE"))
401 0 : return DRIVER_BETWEEN_INCLUSIVE;
402 0 : else if (op == NS_LITERAL_STRING("BETWEEN_INCLUSIVE_START"))
403 0 : return DRIVER_BETWEEN_INCLUSIVE_START;
404 :
405 0 : return DRIVER_UNKNOWN_COMPARISON;
406 : }
407 :
408 : // Arbitrarily returns the first |tagname| child of |element|.
409 : static bool
410 532 : BlacklistNodeGetChildByName(nsIDOMElement *element,
411 : const nsAString& tagname,
412 : nsIDOMNode** firstchild)
413 : {
414 1064 : nsCOMPtr<nsIDOMNodeList> nodelist;
415 1064 : if (NS_FAILED(element->GetElementsByTagName(tagname,
416 : getter_AddRefs(nodelist))) ||
417 532 : !nodelist) {
418 0 : return false;
419 : }
420 :
421 1064 : nsCOMPtr<nsIDOMNode> node;
422 532 : if (NS_FAILED(nodelist->Item(0, getter_AddRefs(node))) || !node)
423 32 : return false;
424 :
425 500 : *firstchild = node.forget().get();
426 500 : return true;
427 : }
428 :
429 : /*
430 :
431 : <gfxBlacklistEntry>
432 : <os>WINNT 6.0</os>
433 : <vendor>0x8086</vendor>
434 : <devices>
435 : <device>0x2582</device>
436 : <device>0x2782</device>
437 : </devices>
438 : <feature> DIRECT3D_10_LAYERS </feature>
439 : <featureStatus> BLOCKED_DRIVER_VERSION </featureStatus>
440 : <driverVersion> 8.52.322.2202 </driverVersion>
441 : <driverVersionComparator> LESS_THAN_OR_EQUAL </driverVersionComparator>
442 : </gfxBlacklistEntry>
443 :
444 : */
445 : static bool
446 76 : BlacklistEntryToDriverInfo(nsIDOMNode* aBlacklistEntry,
447 : GfxDriverInfo& aDriverInfo)
448 : {
449 152 : nsAutoString nodename;
450 304 : if (NS_FAILED(aBlacklistEntry->GetNodeName(nodename)) ||
451 228 : nodename != NS_LITERAL_STRING(BLACKLIST_ENTRY_TAG_NAME)) {
452 0 : return false;
453 : }
454 :
455 152 : nsCOMPtr<nsIDOMElement> element = do_QueryInterface(aBlacklistEntry);
456 76 : if (!element)
457 0 : return false;
458 :
459 152 : nsCOMPtr<nsIDOMNode> dataNode;
460 152 : nsAutoString dataValue;
461 :
462 : // <os>WINNT 6.0</os>
463 152 : if (BlacklistNodeGetChildByName(element, NS_LITERAL_STRING("os"),
464 152 : getter_AddRefs(dataNode))) {
465 76 : BlacklistNodeToTextValue(dataNode, dataValue);
466 76 : aDriverInfo.mOperatingSystem = BlacklistOSToOperatingSystem(dataValue);
467 : }
468 :
469 : // <vendor>0x8086</vendor>
470 152 : if (BlacklistNodeGetChildByName(element, NS_LITERAL_STRING("vendor"),
471 152 : getter_AddRefs(dataNode))) {
472 76 : BlacklistNodeToTextValue(dataNode, dataValue);
473 76 : aDriverInfo.mAdapterVendor = dataValue;
474 : }
475 :
476 : // <devices>
477 : // <device>0x2582</device>
478 : // <device>0x2782</device>
479 : // </devices>
480 152 : if (BlacklistNodeGetChildByName(element, NS_LITERAL_STRING("devices"),
481 152 : getter_AddRefs(dataNode))) {
482 152 : nsCOMPtr<nsIDOMElement> devicesElement = do_QueryInterface(dataNode);
483 76 : if (devicesElement) {
484 :
485 : // Get only the <device> nodes, because BlacklistDevicesToDeviceFamily
486 : // assumes it is passed no other nodes.
487 152 : nsCOMPtr<nsIDOMNodeList> devices;
488 76 : if (NS_SUCCEEDED(devicesElement->GetElementsByTagName(NS_LITERAL_STRING("device"),
489 : getter_AddRefs(devices)))) {
490 76 : GfxDeviceFamily* deviceIds = BlacklistDevicesToDeviceFamily(devices);
491 76 : if (deviceIds) {
492 : // Get GfxDriverInfo to adopt the devices array we created.
493 76 : aDriverInfo.mDeleteDevices = true;
494 76 : aDriverInfo.mDevices = deviceIds;
495 : }
496 : }
497 : }
498 : }
499 :
500 : // <feature> DIRECT3D_10_LAYERS </feature>
501 152 : if (BlacklistNodeGetChildByName(element, NS_LITERAL_STRING("feature"),
502 152 : getter_AddRefs(dataNode))) {
503 76 : BlacklistNodeToTextValue(dataNode, dataValue);
504 76 : aDriverInfo.mFeature = BlacklistFeatureToGfxFeature(dataValue);
505 : }
506 :
507 : // <featureStatus> BLOCKED_DRIVER_VERSION </featureStatus>
508 152 : if (BlacklistNodeGetChildByName(element, NS_LITERAL_STRING("featureStatus"),
509 152 : getter_AddRefs(dataNode))) {
510 76 : BlacklistNodeToTextValue(dataNode, dataValue);
511 76 : aDriverInfo.mFeatureStatus = BlacklistFeatureStatusToGfxFeatureStatus(dataValue);
512 : }
513 :
514 : // <driverVersion> 8.52.322.2202 </driverVersion>
515 152 : if (BlacklistNodeGetChildByName(element, NS_LITERAL_STRING("driverVersion"),
516 152 : getter_AddRefs(dataNode))) {
517 60 : BlacklistNodeToTextValue(dataNode, dataValue);
518 : PRUint64 version;
519 60 : if (ParseDriverVersion(dataValue, &version))
520 60 : aDriverInfo.mDriverVersion = version;
521 : }
522 :
523 : // <driverVersionComparator> LESS_THAN_OR_EQUAL </driverVersionComparator>
524 152 : if (BlacklistNodeGetChildByName(element, NS_LITERAL_STRING("driverVersionComparator"),
525 152 : getter_AddRefs(dataNode))) {
526 60 : BlacklistNodeToTextValue(dataNode, dataValue);
527 60 : aDriverInfo.mComparisonOp = BlacklistComparatorToComparisonOp(dataValue);
528 : }
529 :
530 : // We explicitly ignore unknown elements.
531 :
532 76 : return true;
533 : }
534 :
535 : static void
536 10 : BlacklistEntriesToDriverInfo(nsIDOMNodeList* aBlacklistEntries,
537 : nsTArray<GfxDriverInfo>& aDriverInfo)
538 : {
539 : PRUint32 length;
540 10 : if (NS_FAILED(aBlacklistEntries->GetLength(&length)))
541 0 : return;
542 :
543 10 : aDriverInfo.Clear();
544 10 : aDriverInfo.SetLength(length);
545 86 : for (PRUint32 i = 0; i < length; ++i) {
546 152 : nsCOMPtr<nsIDOMNode> blacklistEntry;
547 152 : if (NS_SUCCEEDED(aBlacklistEntries->Item(i,
548 : getter_AddRefs(blacklistEntry))) &&
549 76 : blacklistEntry) {
550 152 : GfxDriverInfo di;
551 76 : if (BlacklistEntryToDriverInfo(blacklistEntry, di)) {
552 76 : aDriverInfo[i] = di;
553 : }
554 : // Prevent di falling out of scope from destroying the devices.
555 76 : di.mDeleteDevices = false;
556 : }
557 : }
558 : }
559 :
560 : NS_IMETHODIMP
561 10 : GfxInfoBase::Observe(nsISupports* aSubject, const char* aTopic,
562 : const PRUnichar* aData)
563 : {
564 10 : if (strcmp(aTopic, "blocklist-data-gfxItems") == 0) {
565 20 : nsCOMPtr<nsIDOMElement> gfxItems = do_QueryInterface(aSubject);
566 10 : if (gfxItems) {
567 20 : nsCOMPtr<nsIDOMNodeList> blacklistEntries;
568 20 : if (NS_SUCCEEDED(gfxItems->
569 : GetElementsByTagName(NS_LITERAL_STRING(BLACKLIST_ENTRY_TAG_NAME),
570 : getter_AddRefs(blacklistEntries))) &&
571 10 : blacklistEntries)
572 : {
573 20 : nsTArray<GfxDriverInfo> driverInfo;
574 10 : BlacklistEntriesToDriverInfo(blacklistEntries, driverInfo);
575 10 : EvaluateDownloadedBlacklist(driverInfo);
576 : }
577 : }
578 : }
579 :
580 10 : return NS_OK;
581 : }
582 :
583 26 : GfxInfoBase::GfxInfoBase()
584 26 : : mFailureCount(0)
585 : {
586 26 : }
587 :
588 312 : GfxInfoBase::~GfxInfoBase()
589 : {
590 338 : }
591 :
592 : nsresult
593 26 : GfxInfoBase::Init()
594 : {
595 26 : InitGfxDriverInfoShutdownObserver();
596 :
597 52 : nsCOMPtr<nsIObserverService> os = mozilla::services::GetObserverService();
598 26 : if (os) {
599 26 : os->AddObserver(this, "blocklist-data-gfxItems", true);
600 : }
601 :
602 26 : return NS_OK;
603 : }
604 :
605 : NS_IMETHODIMP
606 20 : GfxInfoBase::GetFeatureStatus(PRInt32 aFeature, PRInt32* aStatus NS_OUTPARAM)
607 : {
608 20 : if (GetPrefValueForFeature(aFeature, *aStatus))
609 4 : return NS_OK;
610 :
611 32 : nsString version;
612 32 : nsTArray<GfxDriverInfo> driverInfo;
613 16 : return GetFeatureStatusImpl(aFeature, aStatus, version, driverInfo);
614 : }
615 :
616 : PRInt32
617 96 : GfxInfoBase::FindBlocklistedDeviceInList(const nsTArray<GfxDriverInfo>& info,
618 : nsAString& aSuggestedVersion,
619 : PRInt32 aFeature,
620 : OperatingSystem os)
621 : {
622 96 : PRInt32 status = nsIGfxInfo::FEATURE_STATUS_UNKNOWN;
623 :
624 192 : nsAutoString adapterVendorID;
625 192 : nsAutoString adapterDeviceID;
626 192 : nsAutoString adapterDriverVersionString;
627 288 : if (NS_FAILED(GetAdapterVendorID(adapterVendorID)) ||
628 96 : NS_FAILED(GetAdapterDeviceID(adapterDeviceID)) ||
629 96 : NS_FAILED(GetAdapterDriverVersion(adapterDriverVersionString)))
630 : {
631 0 : return NS_OK;
632 : }
633 :
634 : PRUint64 driverVersion;
635 96 : ParseDriverVersion(adapterDriverVersionString, &driverVersion);
636 :
637 96 : PRUint32 i = 0;
638 688 : for (; i < info.Length(); i++) {
639 1192 : if (info[i].mOperatingSystem != DRIVER_OS_ALL &&
640 596 : info[i].mOperatingSystem != os)
641 : {
642 532 : continue;
643 : }
644 :
645 128 : if (!info[i].mAdapterVendor.Equals(GfxDriverInfo::GetDeviceVendor(VendorAll), nsCaseInsensitiveStringComparator()) &&
646 64 : !info[i].mAdapterVendor.Equals(adapterVendorID, nsCaseInsensitiveStringComparator())) {
647 16 : continue;
648 : }
649 :
650 48 : if (info[i].mDevices != GfxDriverInfo::allDevices && info[i].mDevices->Length()) {
651 48 : bool deviceMatches = false;
652 128 : for (PRUint32 j = 0; j < info[i].mDevices->Length(); j++) {
653 112 : if ((*info[i].mDevices)[j].Equals(adapterDeviceID, nsCaseInsensitiveStringComparator())) {
654 32 : deviceMatches = true;
655 32 : break;
656 : }
657 : }
658 :
659 48 : if (!deviceMatches) {
660 16 : continue;
661 : }
662 : }
663 :
664 32 : bool match = false;
665 :
666 : #if defined(XP_WIN) || defined(ANDROID)
667 : switch (info[i].mComparisonOp) {
668 : case DRIVER_LESS_THAN:
669 : match = driverVersion < info[i].mDriverVersion;
670 : break;
671 : case DRIVER_LESS_THAN_OR_EQUAL:
672 : match = driverVersion <= info[i].mDriverVersion;
673 : break;
674 : case DRIVER_GREATER_THAN:
675 : match = driverVersion > info[i].mDriverVersion;
676 : break;
677 : case DRIVER_GREATER_THAN_OR_EQUAL:
678 : match = driverVersion >= info[i].mDriverVersion;
679 : break;
680 : case DRIVER_EQUAL:
681 : match = driverVersion == info[i].mDriverVersion;
682 : break;
683 : case DRIVER_NOT_EQUAL:
684 : match = driverVersion != info[i].mDriverVersion;
685 : break;
686 : case DRIVER_BETWEEN_EXCLUSIVE:
687 : match = driverVersion > info[i].mDriverVersion && driverVersion < info[i].mDriverVersionMax;
688 : break;
689 : case DRIVER_BETWEEN_INCLUSIVE:
690 : match = driverVersion >= info[i].mDriverVersion && driverVersion <= info[i].mDriverVersionMax;
691 : break;
692 : case DRIVER_BETWEEN_INCLUSIVE_START:
693 : match = driverVersion >= info[i].mDriverVersion && driverVersion < info[i].mDriverVersionMax;
694 : break;
695 : default:
696 : NS_WARNING("Bogus op in GfxDriverInfo");
697 : break;
698 : }
699 : #else
700 : // We don't care what driver version it was. We only check OS version and if
701 : // the device matches.
702 32 : match = true;
703 : #endif
704 :
705 32 : if (match || info[i].mDriverVersion == GfxDriverInfo::allDriverVersions) {
706 64 : if (info[i].mFeature == GfxDriverInfo::allFeatures ||
707 32 : info[i].mFeature == aFeature)
708 : {
709 4 : status = info[i].mFeatureStatus;
710 4 : break;
711 : }
712 : }
713 : }
714 :
715 : // Depends on Windows driver versioning. We don't pass a GfxDriverInfo object
716 : // back to the Windows handler, so we must handle this here.
717 : #if defined(XP_WIN)
718 : if (status == FEATURE_BLOCKED_DRIVER_VERSION) {
719 : if (info[i].mSuggestedVersion) {
720 : aSuggestedVersion.AppendPrintf("%s", info[i].mSuggestedVersion);
721 : } else if (info[i].mComparisonOp == DRIVER_LESS_THAN &&
722 : info[i].mDriverVersion != GfxDriverInfo::allDriverVersions)
723 : {
724 : aSuggestedVersion.AppendPrintf("%lld.%lld.%lld.%lld",
725 : (info[i].mDriverVersion & 0xffff000000000000) >> 48,
726 : (info[i].mDriverVersion & 0x0000ffff00000000) >> 32,
727 : (info[i].mDriverVersion & 0x00000000ffff0000) >> 16,
728 : (info[i].mDriverVersion & 0x000000000000ffff));
729 : }
730 : }
731 : #endif
732 :
733 96 : return status;
734 : }
735 :
736 : nsresult
737 96 : GfxInfoBase::GetFeatureStatusImpl(PRInt32 aFeature,
738 : PRInt32* aStatus,
739 : nsAString& aSuggestedVersion,
740 : const nsTArray<GfxDriverInfo>& aDriverInfo,
741 : OperatingSystem* aOS /* = nsnull */)
742 : {
743 96 : if (*aStatus != nsIGfxInfo::FEATURE_STATUS_UNKNOWN) {
744 : // Terminate now with the status determined by the derived type (OS-specific
745 : // code).
746 0 : return NS_OK;
747 : }
748 :
749 : // If an operating system was provided by the derived GetFeatureStatusImpl,
750 : // grab it here. Otherwise, the OS is unknown.
751 96 : OperatingSystem os = DRIVER_OS_UNKNOWN;
752 96 : if (aOS)
753 96 : os = *aOS;
754 :
755 192 : nsAutoString adapterVendorID;
756 192 : nsAutoString adapterDeviceID;
757 192 : nsAutoString adapterDriverVersionString;
758 288 : if (NS_FAILED(GetAdapterVendorID(adapterVendorID)) ||
759 96 : NS_FAILED(GetAdapterDeviceID(adapterDeviceID)) ||
760 96 : NS_FAILED(GetAdapterDriverVersion(adapterDriverVersionString)))
761 : {
762 0 : return NS_OK;
763 : }
764 :
765 : PRUint64 driverVersion;
766 96 : ParseDriverVersion(adapterDriverVersionString, &driverVersion);
767 :
768 : // Check if the device is blocked from the downloaded blocklist. If not, check
769 : // the static list after that. This order is used so that we can later escape
770 : // out of static blocks (i.e. if we were wrong or something was patched, we
771 : // can back out our static block without doing a release).
772 : PRInt32 status;
773 96 : if (aDriverInfo.Length()) {
774 80 : status = FindBlocklistedDeviceInList(aDriverInfo, aSuggestedVersion, aFeature, os);
775 : } else {
776 16 : if (!mDriverInfo) {
777 8 : mDriverInfo = new nsTArray<GfxDriverInfo>();
778 : }
779 16 : status = FindBlocklistedDeviceInList(GetGfxDriverInfo(), aSuggestedVersion, aFeature, os);
780 : }
781 :
782 : // It's now done being processed. It's safe to set the status to NO_INFO.
783 96 : if (status == nsIGfxInfo::FEATURE_STATUS_UNKNOWN) {
784 92 : *aStatus = nsIGfxInfo::FEATURE_NO_INFO;
785 : } else {
786 4 : *aStatus = status;
787 : }
788 :
789 96 : return NS_OK;
790 : }
791 :
792 : NS_IMETHODIMP
793 0 : GfxInfoBase::GetFeatureSuggestedDriverVersion(PRInt32 aFeature,
794 : nsAString& aVersion NS_OUTPARAM)
795 : {
796 0 : nsCString version;
797 0 : if (GetPrefValueForDriverVersion(version)) {
798 0 : aVersion = NS_ConvertASCIItoUTF16(version);
799 0 : return NS_OK;
800 : }
801 :
802 : PRInt32 status;
803 0 : nsTArray<GfxDriverInfo> driverInfo;
804 0 : return GetFeatureStatusImpl(aFeature, &status, aVersion, driverInfo);
805 : }
806 :
807 :
808 : NS_IMETHODIMP
809 0 : GfxInfoBase::GetWebGLParameter(const nsAString& aParam,
810 : nsAString& aResult NS_OUTPARAM)
811 : {
812 0 : return GfxInfoWebGL::GetWebGLParameter(aParam, aResult);
813 : }
814 :
815 : void
816 10 : GfxInfoBase::EvaluateDownloadedBlacklist(nsTArray<GfxDriverInfo>& aDriverInfo)
817 : {
818 : PRInt32 features[] = {
819 : nsIGfxInfo::FEATURE_DIRECT2D,
820 : nsIGfxInfo::FEATURE_DIRECT3D_9_LAYERS,
821 : nsIGfxInfo::FEATURE_DIRECT3D_10_LAYERS,
822 : nsIGfxInfo::FEATURE_DIRECT3D_10_1_LAYERS,
823 : nsIGfxInfo::FEATURE_OPENGL_LAYERS,
824 : nsIGfxInfo::FEATURE_WEBGL_OPENGL,
825 : nsIGfxInfo::FEATURE_WEBGL_ANGLE,
826 : nsIGfxInfo::FEATURE_WEBGL_MSAA,
827 : 0
828 10 : };
829 :
830 : // For every feature we know about, we evaluate whether this blacklist has a
831 : // non-NO_INFO status. If it does, we set the pref we evaluate in
832 : // GetFeatureStatus above, so we don't need to hold on to this blacklist
833 : // anywhere permanent.
834 10 : int i = 0;
835 100 : while (features[i]) {
836 : PRInt32 status;
837 160 : nsAutoString suggestedVersion;
838 80 : if (NS_SUCCEEDED(GetFeatureStatusImpl(features[i], &status,
839 : suggestedVersion,
840 : aDriverInfo))) {
841 80 : switch (status) {
842 : default:
843 : case nsIGfxInfo::FEATURE_NO_INFO:
844 76 : RemovePrefForFeature(features[i]);
845 76 : break;
846 :
847 : case nsIGfxInfo::FEATURE_BLOCKED_DRIVER_VERSION:
848 4 : if (!suggestedVersion.IsEmpty()) {
849 0 : SetPrefValueForDriverVersion(suggestedVersion);
850 : } else {
851 4 : RemovePrefForDriverVersion();
852 : }
853 : // FALLTHROUGH
854 :
855 : case nsIGfxInfo::FEATURE_BLOCKED_DEVICE:
856 : case nsIGfxInfo::FEATURE_DISCOURAGED:
857 : case nsIGfxInfo::FEATURE_BLOCKED_OS_VERSION:
858 4 : SetPrefValueForFeature(features[i], status);
859 4 : break;
860 : }
861 : }
862 :
863 80 : ++i;
864 : }
865 10 : }
866 :
867 : NS_IMETHODIMP_(void)
868 0 : GfxInfoBase::LogFailure(const nsACString &failure)
869 : {
870 : /* We only keep the first 9 failures */
871 0 : if (mFailureCount < ArrayLength(mFailures)) {
872 0 : mFailures[mFailureCount++] = failure;
873 :
874 : /* record it in the crash notes too */
875 : #if defined(MOZ_CRASHREPORTER)
876 0 : CrashReporter::AppendAppNotesToCrashReport(failure);
877 : #endif
878 : }
879 :
880 0 : }
881 :
882 : /* void getFailures ([optional] out unsigned long failureCount, [array, size_is (failureCount), retval] out string failures); */
883 : /* XPConnect method of returning arrays is very ugly. Would not recommend. Fallable nsMemory::Alloc makes things worse */
884 0 : NS_IMETHODIMP GfxInfoBase::GetFailures(PRUint32 *failureCount NS_OUTPARAM, char ***failures NS_OUTPARAM)
885 : {
886 :
887 0 : NS_ENSURE_ARG_POINTER(failureCount);
888 0 : NS_ENSURE_ARG_POINTER(failures);
889 :
890 0 : *failures = nsnull;
891 0 : *failureCount = mFailureCount;
892 :
893 0 : if (*failureCount != 0) {
894 0 : *failures = (char**)nsMemory::Alloc(*failureCount * sizeof(char*));
895 0 : if (!failures)
896 0 : return NS_ERROR_OUT_OF_MEMORY;
897 :
898 : /* copy over the failure messages into the array we just allocated */
899 0 : for (PRUint32 i = 0; i < *failureCount; i++) {
900 0 : nsCString& flattenedFailureMessage(mFailures[i]);
901 0 : (*failures)[i] = (char*)nsMemory::Clone(flattenedFailureMessage.get(), flattenedFailureMessage.Length() + 1);
902 :
903 0 : if (!(*failures)[i]) {
904 : /* <sarcasm> I'm too afraid to use an inline function... </sarcasm> */
905 0 : NS_FREE_XPCOM_ALLOCATED_POINTER_ARRAY(i, (*failures));
906 0 : return NS_ERROR_OUT_OF_MEMORY;
907 : }
908 : }
909 : }
910 :
911 0 : return NS_OK;
912 : }
913 :
914 : nsTArray<GfxInfoCollectorBase*> *sCollectors;
915 :
916 : static void
917 6 : InitCollectors()
918 : {
919 6 : if (!sCollectors)
920 3 : sCollectors = new nsTArray<GfxInfoCollectorBase*>;
921 6 : }
922 :
923 0 : nsresult GfxInfoBase::GetInfo(JSContext* aCx, jsval* aResult)
924 : {
925 0 : InitCollectors();
926 0 : InfoObject obj(aCx);
927 :
928 0 : for (PRUint32 i = 0; i < sCollectors->Length(); i++) {
929 0 : (*sCollectors)[i]->GetInfo(obj);
930 : }
931 :
932 : // Some example property definitions
933 : // obj.DefineProperty("wordCacheSize", gfxTextRunWordCache::Count());
934 : // obj.DefineProperty("renderer", mRendererIDsString);
935 : // obj.DefineProperty("five", 5);
936 :
937 0 : if (!obj.mOk) {
938 0 : return NS_ERROR_FAILURE;
939 : }
940 :
941 0 : *aResult = OBJECT_TO_JSVAL(obj.mObj);
942 0 : return NS_OK;
943 : }
944 :
945 : void
946 3 : GfxInfoBase::AddCollector(GfxInfoCollectorBase* collector)
947 : {
948 3 : InitCollectors();
949 3 : sCollectors->AppendElement(collector);
950 3 : }
951 :
952 : void
953 3 : GfxInfoBase::RemoveCollector(GfxInfoCollectorBase* collector)
954 : {
955 3 : InitCollectors();
956 3 : for (PRUint32 i = 0; i < sCollectors->Length(); i++) {
957 3 : if ((*sCollectors)[i] == collector) {
958 3 : sCollectors->RemoveElementAt(i);
959 3 : break;
960 : }
961 : }
962 3 : if (sCollectors->IsEmpty()) {
963 3 : delete sCollectors;
964 3 : sCollectors = nsnull;
965 : }
966 3 : }
967 :
968 3 : GfxInfoCollectorBase::GfxInfoCollectorBase()
969 : {
970 3 : GfxInfoBase::AddCollector(this);
971 3 : }
972 :
973 3 : GfxInfoCollectorBase::~GfxInfoCollectorBase()
974 : {
975 3 : GfxInfoBase::RemoveCollector(this);
976 6 : }
|