--- a/toolkit/mozapps/extensions/nsBlocklistService.js
+++ b/toolkit/mozapps/extensions/nsBlocklistService.js
@@ -323,16 +323,17 @@ Blocklist.prototype = {
* Each value in the version range array is a JS Object that
* has the following properties:
* "minVersion" The minimum version in a version range
* (default = 0)
* "maxVersion" The maximum version in a version range
* (default = *)
*/
_addonEntries: null,
+ _gfxEntries: null,
_pluginEntries: null,
shutdown: function() {
Services.obs.removeObserver(this, "xpcom-shutdown");
Services.ppmm.removeMessageListener("Blocklist:getPluginBlocklistState", this);
Services.ppmm.removeMessageListener("Blocklist:content-blocklist-updated", this);
gPref.removeObserver("extensions.blocklist.", this);
gPref.removeObserver(PREF_EM_LOGGING_ENABLED, this);
@@ -655,19 +656,22 @@ Blocklist.prototype = {
(request.status != 200 && request.status != 0)) {
LOG("Blocklist::onXMLLoad: there was an error during load");
return;
}
var oldAddonEntries = this._addonEntries;
var oldPluginEntries = this._pluginEntries;
this._addonEntries = [];
+ this._gfxEntries = [];
this._pluginEntries = [];
this._loadBlocklistFromString(request.responseText);
+ // We don't inform the users when the graphics blocklist changed at runtime.
+ // However addons and plugins blocking status is refreshed.
this._blocklistUpdated(oldAddonEntries, oldPluginEntries);
try {
let path = OS.Path.join(OS.Constants.Path.profileDir, FILE_BLOCKLIST);
yield OS.File.writeAtomic(path, request.responseText, {tmpPath: path + ".tmp"});
} catch (e) {
LOG("Blocklist::onXMLLoad: " + e);
}
@@ -696,16 +700,17 @@ Blocklist.prototype = {
},
/**
* Finds the newest blocklist file from the application and the profile and
* load it or does nothing if neither exist.
*/
_loadBlocklist: function() {
this._addonEntries = [];
+ this._gfxEntries = [];
this._pluginEntries = [];
var profFile = FileUtils.getFile(KEY_PROFILEDIR, [FILE_BLOCKLIST]);
if (profFile.exists()) {
this._loadBlocklistFromFile(profFile);
return;
}
var appFile = FileUtils.getFile(KEY_APPDIR, [FILE_BLOCKLIST]);
if (appFile.exists()) {
@@ -832,26 +837,27 @@ Blocklist.prototype = {
fstream.close();
}
if (text)
this._loadBlocklistFromString(text);
},
_isBlocklistLoaded: function() {
- return this._addonEntries != null && this._pluginEntries != null;
+ return this._addonEntries != null && this._gfxEntries != null && this._pluginEntries != null;
},
_isBlocklistPreloaded: function() {
return this._preloadedBlocklistContent != null;
},
/* Used for testing */
_clear: function() {
this._addonEntries = null;
+ this._gfxEntries = null;
this._pluginEntries = null;
this._preloadedBlocklistContent = null;
},
_preloadBlocklist: Task.async(function*() {
let profPath = OS.Path.join(OS.Constants.Path.profileDir, FILE_BLOCKLIST);
try {
yield this._preloadBlocklistFile(profPath);
@@ -907,57 +913,62 @@ Blocklist.prototype = {
var childNodes = doc.documentElement.childNodes;
for (let element of childNodes) {
if (!(element instanceof Ci.nsIDOMElement))
continue;
switch (element.localName) {
case "emItems":
// Special case for b2g, since we don't use the addon manager.
if (AppConstants.MOZ_B2G) {
- let extensions = this._processItemNodes(element.childNodes, "em",
+ let extensions = this._processItemNodes(element.childNodes, "emItem",
this._handleEmItemNode);
DOMApplicationRegistry.blockExtensions(extensions);
return;
}
- this._addonEntries = this._processItemNodes(element.childNodes, "em",
+ this._addonEntries = this._processItemNodes(element.childNodes, "emItem",
this._handleEmItemNode);
break;
case "pluginItems":
// We don't support plugins on b2g.
if (AppConstants.MOZ_B2G) {
return;
}
- this._pluginEntries = this._processItemNodes(element.childNodes, "plugin",
+ this._pluginEntries = this._processItemNodes(element.childNodes, "pluginItem",
this._handlePluginItemNode);
break;
case "certItems":
if (populateCertBlocklist) {
- this._processItemNodes(element.childNodes, "cert",
+ this._processItemNodes(element.childNodes, "certItem",
this._handleCertItemNode.bind(this));
}
break;
+ case "gfxItems":
+ // Parse as simple list of objects.
+ this._gfxEntries = this._processItemNodes(element.childNodes, "gfxBlacklistEntry",
+ this._handleGfxBlacklistNode);
+ break;
default:
- Services.obs.notifyObservers(element,
- "blocklist-data-" + element.localName,
- null);
+ LOG("Blocklist::_loadBlocklistFromString: ignored entries " + element.localName);
}
}
if (populateCertBlocklist) {
gCertBlocklistService.saveEntries();
}
+ if (this._gfxEntries.length > 0) {
+ this._notifyObserversBlocklistGFX();
+ }
}
catch (e) {
LOG("Blocklist::_loadBlocklistFromFile: Error constructing blocklist " + e);
return;
}
},
- _processItemNodes: function(itemNodes, prefix, handler) {
+ _processItemNodes: function(itemNodes, itemName, handler) {
var result = [];
- var itemName = prefix + "Item";
for (var i = 0; i < itemNodes.length; ++i) {
var blocklistElement = itemNodes.item(i);
if (!(blocklistElement instanceof Ci.nsIDOMElement) ||
blocklistElement.localName != itemName)
continue;
handler(blocklistElement, result);
}
@@ -1084,16 +1095,85 @@ Blocklist.prototype = {
if (blockEntry.versions.length == 0)
blockEntry.versions.push(new BlocklistItemData(null));
blockEntry.blockID = blocklistElement.getAttribute("blockID");
result.push(blockEntry);
},
+ // <gfxBlacklistEntry blockID="g60">
+ // <os>WINNT 6.0</os>
+ // <osversion>14</osversion> currently only used for Android
+ // <versionRange minVersion="42.0" maxVersion="13.0b2"/>
+ // <vendor>0x8086</vendor>
+ // <devices>
+ // <device>0x2582</device>
+ // <device>0x2782</device>
+ // </devices>
+ // <feature> DIRECT3D_10_LAYERS </feature>
+ // <featureStatus> BLOCKED_DRIVER_VERSION </featureStatus>
+ // <driverVersion> 8.52.322.2202 </driverVersion>
+ // <driverVersionMax> 8.52.322.2202 </driverVersionMax>
+ // <driverVersionComparator> LESS_THAN_OR_EQUAL </driverVersionComparator>
+ // <model>foo</model>
+ // <product>foo</product>
+ // <manufacturer>foo</manufacturer>
+ // <hardware>foo</hardware>
+ // </gfxBlacklistEntry>
+ _handleGfxBlacklistNode: function (blocklistElement, result) {
+ const blockEntry = {};
+
+ // The blockID attribute is always present in the actual data produced on server
+ // (see https://github.com/mozilla/addons-server/blob/2016.05.05/src/olympia/blocklist/templates/blocklist/blocklist.xml#L74)
+ // But it is sometimes missing in test fixtures.
+ if (blocklistElement.hasAttribute("blockID")) {
+ blockEntry.blockID = blocklistElement.getAttribute("blockID");
+ }
+
+ // Trim helper (spaces, tabs, no-break spaces..)
+ const trim = (s) => (s || '').replace(/(^[\s\uFEFF\xA0]+)|([\s\uFEFF\xA0]+$)/g, "");
+
+ for (let i = 0; i < blocklistElement.childNodes.length; ++i) {
+ var matchElement = blocklistElement.childNodes.item(i);
+ if (!(matchElement instanceof Ci.nsIDOMElement))
+ continue;
+
+ let value;
+ if (matchElement.localName == "devices") {
+ value = [];
+ for (let j = 0; j < matchElement.childNodes.length; j++) {
+ const childElement = matchElement.childNodes.item(j);
+ const childValue = trim(childElement.textContent);
+ // Make sure no empty value is added.
+ if (childValue) {
+ if (/,/.test(childValue)) {
+ // Devices can't contain comma.
+ // (c.f serialization in _notifyObserversBlocklistGFX)
+ const e = new Error(`Unsupported device name ${childValue}`);
+ Components.utils.reportError(e);
+ }
+ else {
+ value.push(childValue);
+ }
+ }
+ }
+ } else if (matchElement.localName == "versionRange") {
+ value = {minVersion: trim(matchElement.getAttribute("minVersion")) || "0",
+ maxVersion: trim(matchElement.getAttribute("maxVersion")) || "*"};
+ } else {
+ value = trim(matchElement.textContent);
+ }
+ if (value) {
+ blockEntry[matchElement.localName] = value;
+ }
+ }
+ result.push(blockEntry);
+ },
+
/* See nsIBlocklistService */
getPluginBlocklistState: function(plugin, appVersion, toolkitVersion) {
if (AppConstants.platform == "android" ||
AppConstants.MOZ_B2G) {
return Ci.nsIBlocklistService.STATE_NOT_BLOCKED;
} else {
if (!this._isBlocklistLoaded())
this._loadBlocklist();
@@ -1226,16 +1306,34 @@ Blocklist.prototype = {
let {entry: blockEntry, version: blockEntryVersion} = r;
if (!blockEntry.blockID) {
return null;
}
return blockEntry.infoURL;
},
+ _notifyObserversBlocklistGFX: function () {
+ // Notify `GfxInfoBase`, by passing a string serialization.
+ // This way we avoid spreading XML structure logics there.
+ const payload = this._gfxEntries.map((r) => {
+ return Object.keys(r).sort().filter((k) => !/id|last_modified/.test(k)).map((key) => {
+ let value = r[key];
+ if (Array.isArray(value)) {
+ value = value.join(",");
+ } else if (value.hasOwnProperty("minVersion")) {
+ // When XML is parsed, both minVersion and maxVersion are set.
+ value = `${value.minVersion},${value.maxVersion}`;
+ }
+ return `${key}:${value}`;
+ }).join("\t");
+ }).join("\n");
+ Services.obs.notifyObservers(null, "blocklist-data-gfxItems", payload);
+ },
+
_notifyObserversBlocklistUpdated: function() {
Services.obs.notifyObservers(this, "blocklist-updated", "");
Services.ppmm.broadcastAsyncMessage("Blocklist:blocklistInvalidated", {});
},
_blocklistUpdated: function(oldAddonEntries, oldPluginEntries) {
if (AppConstants.MOZ_B2G) {
return;
new file mode 100644
--- /dev/null
+++ b/toolkit/mozapps/extensions/test/xpcshell/test_blocklist_gfx.js
@@ -0,0 +1,157 @@
+const { classes: Cc, interfaces: Ci, utils: Cu } = Components;
+
+Cu.import("resource://gre/modules/Services.jsm");
+Cu.import("resource://gre/modules/XPCOMUtils.jsm");
+
+const TEST_APP_ID = "xpcshell@tests.mozilla.org";
+
+
+const EVENT_NAME = "blocklist-data-gfxItems";
+
+const SAMPLE_GFX_RECORD = {
+ "driverVersionComparator": "LESS_THAN_OR_EQUAL",
+ "driverVersion": "8.17.12.5896",
+ "vendor": "0x10de",
+ "blockID": "g36",
+ "feature": "DIRECT3D_9_LAYERS",
+ "devices": ["0x0a6c", "geforce"],
+ "featureStatus": "BLOCKED_DRIVER_VERSION",
+ "last_modified": 1458035931837,
+ "os": "WINNT 6.1",
+ "id": "3f947f16-37c2-4e96-d356-78b26363729b",
+ "versionRange": {"minVersion": 0, "maxVersion": "*"}
+};
+
+
+function Blocklist() {
+ let blocklist = Cc["@mozilla.org/extensions/blocklist;1"].
+ getService().wrappedJSObject;
+ blocklist._clear();
+ return blocklist;
+}
+
+
+function run_test() {
+ run_next_test();
+}
+
+
+add_task(function* test_sends_serialized_data() {
+ const blocklist = Blocklist();
+ blocklist._gfxEntries = [SAMPLE_GFX_RECORD];
+
+ const expected = "blockID:g36\tdevices:0x0a6c,geforce\tdriverVersion:8.17.12.5896\t" +
+ "driverVersionComparator:LESS_THAN_OR_EQUAL\tfeature:DIRECT3D_9_LAYERS\t" +
+ "featureStatus:BLOCKED_DRIVER_VERSION\tos:WINNT 6.1\tvendor:0x10de\t" +
+ "versionRange:0,*";
+ let received;
+ const observe = (subject, topic, data) => { received = data };
+ Services.obs.addObserver(observe, EVENT_NAME, false);
+ blocklist._notifyObserversBlocklistGFX();
+ equal(received, expected);
+ Services.obs.removeObserver(observe, EVENT_NAME);
+});
+
+
+add_task(function* test_parsing_fails_if_devices_contains_comma() {
+ const input = "<blocklist xmlns=\"http://www.mozilla.org/2006/addons-blocklist\">" +
+ "<gfxItems>" +
+ " <gfxBlacklistEntry>" +
+ " <devices>" +
+ " <device>0x2,582</device>" +
+ " <device>0x2782</device>" +
+ " </devices>" +
+ " </gfxBlacklistEntry>" +
+ "</gfxItems>" +
+ "</blocklist>";
+ const blocklist = Blocklist();
+ blocklist._loadBlocklistFromString(input);
+ equal(blocklist._gfxEntries[0].devices.length, 1);
+ equal(blocklist._gfxEntries[0].devices[0], "0x2782");
+});
+
+
+add_task(function* test_empty_values_are_ignored() {
+ const input = "<blocklist xmlns=\"http://www.mozilla.org/2006/addons-blocklist\">" +
+ "<gfxItems>" +
+ " <gfxBlacklistEntry>" +
+ " <os></os>" +
+ " </gfxBlacklistEntry>" +
+ "</gfxItems>" +
+ "</blocklist>";
+ const blocklist = Blocklist();
+ let received;
+ const observe = (subject, topic, data) => { received = data };
+ Services.obs.addObserver(observe, EVENT_NAME, false);
+ blocklist._loadBlocklistFromString(input);
+ ok(received.indexOf("os" < 0));
+ Services.obs.removeObserver(observe, EVENT_NAME);
+});
+
+add_task(function* test_empty_devices_are_ignored() {
+ const input = "<blocklist xmlns=\"http://www.mozilla.org/2006/addons-blocklist\">" +
+ "<gfxItems>" +
+ " <gfxBlacklistEntry>" +
+ " <devices></devices>" +
+ " </gfxBlacklistEntry>" +
+ "</gfxItems>" +
+ "</blocklist>";
+ const blocklist = Blocklist();
+ let received;
+ const observe = (subject, topic, data) => { received = data };
+ Services.obs.addObserver(observe, EVENT_NAME, false);
+ blocklist._loadBlocklistFromString(input);
+ ok(received.indexOf("devices" < 0));
+ Services.obs.removeObserver(observe, EVENT_NAME);
+});
+
+add_task(function* test_version_range_default_values() {
+ const input = "<blocklist xmlns=\"http://www.mozilla.org/2006/addons-blocklist\">" +
+ "<gfxItems>" +
+ " <gfxBlacklistEntry>" +
+ " <versionRange minVersion=\"13.0b2\" maxVersion=\"42.0\"/>" +
+ " </gfxBlacklistEntry>" +
+ " <gfxBlacklistEntry>" +
+ " <versionRange maxVersion=\"2.0\"/>" +
+ " </gfxBlacklistEntry>" +
+ " <gfxBlacklistEntry>" +
+ " <versionRange minVersion=\"1.0\"/>" +
+ " </gfxBlacklistEntry>" +
+ " <gfxBlacklistEntry>" +
+ " <versionRange minVersion=\" \"/>" +
+ " </gfxBlacklistEntry>" +
+ " <gfxBlacklistEntry>" +
+ " <versionRange/>" +
+ " </gfxBlacklistEntry>" +
+ "</gfxItems>" +
+ "</blocklist>";
+ const blocklist = Blocklist();
+ blocklist._loadBlocklistFromString(input);
+ equal(blocklist._gfxEntries[0].versionRange.minVersion, "13.0b2");
+ equal(blocklist._gfxEntries[0].versionRange.maxVersion, "42.0");
+ equal(blocklist._gfxEntries[1].versionRange.minVersion, "0");
+ equal(blocklist._gfxEntries[1].versionRange.maxVersion, "2.0");
+ equal(blocklist._gfxEntries[2].versionRange.minVersion, "1.0");
+ equal(blocklist._gfxEntries[2].versionRange.maxVersion, "*");
+ equal(blocklist._gfxEntries[3].versionRange.minVersion, "0");
+ equal(blocklist._gfxEntries[3].versionRange.maxVersion, "*");
+ equal(blocklist._gfxEntries[4].versionRange.minVersion, "0");
+ equal(blocklist._gfxEntries[4].versionRange.maxVersion, "*");
+});
+
+add_task(function* test_blockid_attribute() {
+ const input = "<blocklist xmlns=\"http://www.mozilla.org/2006/addons-blocklist\">" +
+ "<gfxItems>" +
+ " <gfxBlacklistEntry blockID=\"g60\">" +
+ " <vendor> 0x10de </vendor>" +
+ " </gfxBlacklistEntry>" +
+ " <gfxBlacklistEntry>" +
+ " <feature> DIRECT3D_9_LAYERS </feature>" +
+ " </gfxBlacklistEntry>" +
+ "</gfxItems>" +
+ "</blocklist>";
+ const blocklist = Blocklist();
+ blocklist._loadBlocklistFromString(input);
+ equal(blocklist._gfxEntries[0].blockID, "g60");
+ ok(!blocklist._gfxEntries[1].hasOwnProperty("blockID"));
+});
--- a/toolkit/mozapps/extensions/test/xpcshell/test_gfxBlacklist_prefs.js
+++ b/toolkit/mozapps/extensions/test/xpcshell/test_gfxBlacklist_prefs.js
@@ -9,16 +9,17 @@ var { classes: Cc, interfaces: Ci } = Co
// Uses test_gfxBlacklist.xml and test_gfxBlacklist2.xml
Components.utils.import("resource://testing-common/httpd.js");
var gTestserver = new HttpServer();
gTestserver.start(-1);
gPort = gTestserver.identity.primaryPort;
mapFile("/data/test_gfxBlacklist.xml", gTestserver);
+mapFile("/data/test_gfxBlacklist2.xml", gTestserver);
function get_platform() {
var xulRuntime = Components.classes["@mozilla.org/xre/app-info;1"]
.getService(Components.interfaces.nsIXULRuntime);
return xulRuntime.OS;
}
function load_blocklist(file) {
--- a/toolkit/mozapps/extensions/test/xpcshell/xpcshell.ini
+++ b/toolkit/mozapps/extensions/test/xpcshell/xpcshell.ini
@@ -6,16 +6,17 @@ tail =
firefox-appdir = browser
dupe-manifest =
support-files =
data/**
xpcshell-shared.ini
[test_addon_path_service.js]
[test_asyncBlocklistLoad.js]
+[test_blocklist_gfx.js]
[test_cache_certdb.js]
run-if = addon_signing
[test_cacheflush.js]
[test_DeferredSave.js]
[test_gmpProvider.js]
skip-if = appname != "firefox"
[test_hotfix_cert.js]
[test_isReady.js]
--- a/widget/GfxInfoBase.cpp
+++ b/widget/GfxInfoBase.cpp
@@ -233,52 +233,16 @@ SetPrefValueForDriverVersion(const nsASt
}
static void
RemovePrefForDriverVersion()
{
Preferences::ClearUser(SUGGESTED_VERSION_PREF);
}
-// <foo>Hello</foo> - "Hello" is stored as a child text node of the foo node.
-static bool
-BlacklistNodeToTextValue(nsIDOMNode *aBlacklistNode, nsAString& aValue)
-{
- nsAutoString value;
- if (NS_FAILED(aBlacklistNode->GetTextContent(value)))
- return false;
-
- value.Trim(" \t\r\n");
- aValue = value;
-
- return true;
-}
-
-// <foo attr=Hello/> finds "Hello" if the aAttrName is "attr".
-static bool
-BlacklistAttrToTextValue(nsIDOMNode *aBlacklistNode,
- const nsAString& aAttrName,
- nsAString& aValue)
-{
- nsCOMPtr<nsIDOMElement> element = do_QueryInterface(aBlacklistNode);
- if (!element) {
- return false;
- }
-
- nsAutoString value;
- if (NS_FAILED(element->GetAttribute(aAttrName, value))) {
- return false;
- }
-
- value.Trim(" \t\r\n");
- aValue = value;
-
- return true;
-}
-
static OperatingSystem
BlacklistOSToOperatingSystem(const nsAString& os)
{
if (os.EqualsLiteral("WINNT 5.1"))
return DRIVER_OS_WINDOWS_XP;
else if (os.EqualsLiteral("WINNT 5.2"))
return DRIVER_OS_WINDOWS_SERVER_2003;
@@ -312,36 +276,29 @@ BlacklistOSToOperatingSystem(const nsASt
return DRIVER_OS_ANDROID;
else if (os.EqualsLiteral("All"))
return DRIVER_OS_ALL;
return DRIVER_OS_UNKNOWN;
}
static GfxDeviceFamily*
-BlacklistDevicesToDeviceFamily(nsIDOMHTMLCollection* aDevices)
+BlacklistDevicesToDeviceFamily(nsTArray<nsCString>& devices)
{
- uint32_t length;
- if (NS_FAILED(aDevices->GetLength(&length)))
+ if (devices.Length() == 0)
return nullptr;
- // For each <device>, get its device ID, and return a freshly-allocated
+ // For each device, get its device ID, and return a freshly-allocated
// GfxDeviceFamily with the contents of that array.
GfxDeviceFamily* deviceIds = new GfxDeviceFamily;
- for (uint32_t i = 0; i < length; ++i) {
- nsCOMPtr<nsIDOMNode> node;
- if (NS_FAILED(aDevices->Item(i, getter_AddRefs(node))) || !node)
- continue;
-
- nsAutoString deviceValue;
- if (!BlacklistNodeToTextValue(node, deviceValue))
- continue;
-
- deviceIds->AppendElement(deviceValue);
+ for (uint32_t i = 0; i < devices.Length(); ++i) {
+ // We make sure we don't add any "empty" device entries to the array, so
+ // we don't need to check if devices[i] is empty.
+ deviceIds->AppendElement(NS_ConvertUTF8toUTF16(devices[i]));
}
return deviceIds;
}
static int32_t
BlacklistFeatureToGfxFeature(const nsAString& aFeature)
{
@@ -427,278 +384,172 @@ BlacklistComparatorToComparisonOp(const
else if (op.EqualsLiteral("BETWEEN_INCLUSIVE"))
return DRIVER_BETWEEN_INCLUSIVE;
else if (op.EqualsLiteral("BETWEEN_INCLUSIVE_START"))
return DRIVER_BETWEEN_INCLUSIVE_START;
return DRIVER_COMPARISON_IGNORED;
}
-// Arbitrarily returns the first |tagname| child of |element|.
-static bool
-BlacklistNodeGetChildByName(nsIDOMElement *element,
- const nsAString& tagname,
- nsIDOMNode** firstchild)
-{
- nsCOMPtr<nsIDOMHTMLCollection> nodelist;
- if (NS_FAILED(element->GetElementsByTagName(tagname,
- getter_AddRefs(nodelist))) ||
- !nodelist) {
- return false;
- }
-
- nsCOMPtr<nsIDOMNode> node;
- if (NS_FAILED(nodelist->Item(0, getter_AddRefs(node))) || !node)
- return false;
-
- node.forget(firstchild);
- return true;
-}
/*
-
-<gfxBlacklistEntry>
- <os>WINNT 6.0</os>
- <vendor>0x8086</vendor>
- <devices>
- <device>0x2582</device>
- <device>0x2782</device>
- </devices>
- <feature> DIRECT3D_10_LAYERS </feature>
- <featureStatus> BLOCKED_DRIVER_VERSION </featureStatus>
- <driverVersion> 8.52.322.2202 </driverVersion>
- <driverVersionComparator> LESS_THAN_OR_EQUAL </driverVersionComparator>
-</gfxBlacklistEntry>
-
+ Deserialize Blacklist entries from string.
+ e.g:
+ 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
*/
static bool
-BlacklistEntryToDriverInfo(nsIDOMNode* aBlacklistEntry,
+BlacklistEntryToDriverInfo(nsCString& aBlacklistEntry,
GfxDriverInfo& aDriverInfo)
{
- nsAutoString nodename;
- if (NS_FAILED(aBlacklistEntry->GetNodeName(nodename)) ||
- nodename != NS_LITERAL_STRING(BLACKLIST_ENTRY_TAG_NAME)) {
- return false;
- }
-
- nsCOMPtr<nsIDOMElement> element = do_QueryInterface(aBlacklistEntry);
- if (!element)
- return false;
-
- nsCOMPtr<nsIDOMNode> dataNode;
- nsAutoString dataValue;
-
// If we get an application version to be zero, something is not working
// and we are not going to bother checking the blocklist versions.
// See TestGfxWidgets.cpp for how version comparison works.
// <versionRange minVersion="42.0a1" maxVersion="45.0"></versionRange>
static mozilla::Version zeroV("0");
static mozilla::Version appV(GfxInfoBase::GetApplicationVersion().get());
if (appV <= zeroV) {
gfxCriticalErrorOnce(gfxCriticalError::DefaultOptions(false)) << "Invalid application version " << GfxInfoBase::GetApplicationVersion().get();
- } else if (BlacklistNodeGetChildByName(element,
- NS_LITERAL_STRING("versionRange"),
- getter_AddRefs(dataNode))) {
- if (BlacklistAttrToTextValue(dataNode,
- NS_LITERAL_STRING("minVersion"),
- dataValue)) {
- mozilla::Version minV(NS_ConvertUTF16toUTF8(dataValue).get());
- if (minV > zeroV && appV < minV) {
+ }
+
+ nsTArray<nsCString> keyValues;
+ ParseString(aBlacklistEntry, '\t', keyValues);
+
+ aDriverInfo.mRuleId = NS_LITERAL_CSTRING("FEATURE_FAILURE_DL_BLACKLIST_NO_ID");
+
+ for (uint32_t i = 0; i < keyValues.Length(); ++i) {
+ nsCString keyValue = keyValues[i];
+ nsTArray<nsCString> splitted;
+ ParseString(keyValue, ':', splitted);
+ if (splitted.Length() != 2) {
+ // If we don't recognize the input data, we do not want to proceed.
+ gfxCriticalErrorOnce(CriticalLog::DefaultOptions(false)) << "Unrecognized data " << keyValue.get();
+ return false;
+ }
+ nsCString key = splitted[0];
+ nsCString value = splitted[1];
+ NS_ConvertUTF8toUTF16 dataValue(value);
+
+ if (value.Length() == 0) {
+ // Safety check for empty values.
+ gfxCriticalErrorOnce(CriticalLog::DefaultOptions(false)) << "Empty value for " << key.get();
+ return false;
+ }
+
+ if (key.EqualsLiteral("blockID")) {
+ nsCString blockIdStr = NS_LITERAL_CSTRING("FEATURE_FAILURE_DL_BLACKLIST_") + value;
+ aDriverInfo.mRuleId = blockIdStr.get();
+ } else if (key.EqualsLiteral("os")) {
+ aDriverInfo.mOperatingSystem = BlacklistOSToOperatingSystem(dataValue);
+ } else if (key.EqualsLiteral("osversion")) {
+ aDriverInfo.mOperatingSystemVersion = strtoul(value.get(), nullptr, 10);
+ } else if (key.EqualsLiteral("vendor")) {
+ aDriverInfo.mAdapterVendor = dataValue;
+ } else if (key.EqualsLiteral("feature")) {
+ aDriverInfo.mFeature = BlacklistFeatureToGfxFeature(dataValue);
+ if (aDriverInfo.mFeature < 0) {
+ // If we don't recognize the feature, we do not want to proceed.
+ gfxCriticalErrorOnce(CriticalLog::DefaultOptions(false)) << "Unrecognized feature " << value.get();
+ return false;
+ }
+ } else if (key.EqualsLiteral("featureStatus")) {
+ aDriverInfo.mFeatureStatus = BlacklistFeatureStatusToGfxFeatureStatus(dataValue);
+ } else if (key.EqualsLiteral("driverVersion")) {
+ uint64_t version;
+ if (ParseDriverVersion(dataValue, &version))
+ aDriverInfo.mDriverVersion = version;
+ } else if (key.EqualsLiteral("driverVersionMax")) {
+ uint64_t version;
+ if (ParseDriverVersion(dataValue, &version))
+ aDriverInfo.mDriverVersionMax = version;
+ } else if (key.EqualsLiteral("driverVersionMax")) {
+ uint64_t version;
+ if (ParseDriverVersion(dataValue, &version))
+ aDriverInfo.mDriverVersionMax = version;
+ } else if (key.EqualsLiteral("driverVersionComparator")) {
+ aDriverInfo.mComparisonOp = BlacklistComparatorToComparisonOp(dataValue);
+ } else if (key.EqualsLiteral("model")) {
+ aDriverInfo.mModel = dataValue;
+ } else if (key.EqualsLiteral("product")) {
+ aDriverInfo.mProduct = dataValue;
+ } else if (key.EqualsLiteral("manufacturer")) {
+ aDriverInfo.mManufacturer = dataValue;
+ } else if (key.EqualsLiteral("hardware")) {
+ aDriverInfo.mHardware = dataValue;
+ } else if (key.EqualsLiteral("versionRange")) {
+ nsTArray<nsCString> versionRange;
+ ParseString(value, ',', versionRange);
+ if (versionRange.Length() != 2) {
+ gfxCriticalErrorOnce(CriticalLog::DefaultOptions(false)) << "Unrecognized versionRange " << value.get();
+ return false;
+ }
+ nsCString minValue = versionRange[0];
+ nsCString maxValue = versionRange[1];
+
+ mozilla::Version minV(minValue.get());
+ mozilla::Version maxV(maxValue.get());
+
+ if (minV > zeroV && !(appV >= minV)) {
// The version of the application is less than the minimal version
// this blocklist entry applies to, so we can just ignore it by
// returning false and letting the caller deal with it.
return false;
}
- }
-
- if (BlacklistAttrToTextValue(dataNode,
- NS_LITERAL_STRING("maxVersion"),
- dataValue)) {
- mozilla::Version maxV(NS_ConvertUTF16toUTF8(dataValue).get());
- if (maxV > zeroV && appV > maxV) {
+ if (maxV > zeroV && !(appV <= maxV)) {
// The version of the application is more than the maximal version
// this blocklist entry applies to, so we can just ignore it by
// returning false and letting the caller deal with it.
return false;
}
- }
- }
-
- // <os>WINNT 6.0</os>
- if (BlacklistNodeGetChildByName(element, NS_LITERAL_STRING("os"),
- getter_AddRefs(dataNode))) {
- BlacklistNodeToTextValue(dataNode, dataValue);
- aDriverInfo.mOperatingSystem = BlacklistOSToOperatingSystem(dataValue);
- }
-
- // <osversion>14</osversion> currently only used for Android
- if (BlacklistNodeGetChildByName(element, NS_LITERAL_STRING("osversion"),
- getter_AddRefs(dataNode))) {
- BlacklistNodeToTextValue(dataNode, dataValue);
- aDriverInfo.mOperatingSystemVersion = strtoul(NS_LossyConvertUTF16toASCII(dataValue).get(),
- nullptr, 10);
- }
-
- // <vendor>0x8086</vendor>
- if (BlacklistNodeGetChildByName(element, NS_LITERAL_STRING("vendor"),
- getter_AddRefs(dataNode))) {
- BlacklistNodeToTextValue(dataNode, dataValue);
- aDriverInfo.mAdapterVendor = dataValue;
- }
-
- // <devices>
- // <device>0x2582</device>
- // <device>0x2782</device>
- // </devices>
- if (BlacklistNodeGetChildByName(element, NS_LITERAL_STRING("devices"),
- getter_AddRefs(dataNode))) {
- nsCOMPtr<nsIDOMElement> devicesElement = do_QueryInterface(dataNode);
- if (devicesElement) {
-
- // Get only the <device> nodes, because BlacklistDevicesToDeviceFamily
- // assumes it is passed no other nodes.
- nsCOMPtr<nsIDOMHTMLCollection> devices;
- if (NS_SUCCEEDED(devicesElement->GetElementsByTagName(NS_LITERAL_STRING("device"),
- getter_AddRefs(devices)))) {
- GfxDeviceFamily* deviceIds = BlacklistDevicesToDeviceFamily(devices);
- if (deviceIds) {
- // Get GfxDriverInfo to adopt the devices array we created.
- aDriverInfo.mDeleteDevices = true;
- aDriverInfo.mDevices = deviceIds;
- }
+ } else if (key.EqualsLiteral("devices")) {
+ nsTArray<nsCString> devices;
+ ParseString(value, ',', devices);
+ GfxDeviceFamily* deviceIds = BlacklistDevicesToDeviceFamily(devices);
+ if (deviceIds) {
+ // Get GfxDriverInfo to adopt the devices array we created.
+ aDriverInfo.mDeleteDevices = true;
+ aDriverInfo.mDevices = deviceIds;
}
}
- }
-
- // <feature> DIRECT3D_10_LAYERS </feature>
- if (BlacklistNodeGetChildByName(element, NS_LITERAL_STRING("feature"),
- getter_AddRefs(dataNode))) {
- BlacklistNodeToTextValue(dataNode, dataValue);
- aDriverInfo.mFeature = BlacklistFeatureToGfxFeature(dataValue);
- if (aDriverInfo.mFeature < 0) {
- // If we don't recognize the feature, we do not want to proceed.
- gfxWarning() << "Unrecognized feature " << NS_ConvertUTF16toUTF8(dataValue).get();
- return false;
- }
- }
-
- // <featureStatus> BLOCKED_DRIVER_VERSION </featureStatus>
- if (BlacklistNodeGetChildByName(element, NS_LITERAL_STRING("featureStatus"),
- getter_AddRefs(dataNode))) {
- BlacklistNodeToTextValue(dataNode, dataValue);
- aDriverInfo.mFeatureStatus = BlacklistFeatureStatusToGfxFeatureStatus(dataValue);
- }
-
- // <driverVersion> 8.52.322.2202 </driverVersion>
- if (BlacklistNodeGetChildByName(element, NS_LITERAL_STRING("driverVersion"),
- getter_AddRefs(dataNode))) {
- BlacklistNodeToTextValue(dataNode, dataValue);
- uint64_t version;
- if (ParseDriverVersion(dataValue, &version))
- aDriverInfo.mDriverVersion = version;
- }
-
- // <driverVersionMax> 8.52.322.2202 </driverVersionMax>
- if (BlacklistNodeGetChildByName(element, NS_LITERAL_STRING("driverVersionMax"),
- getter_AddRefs(dataNode))) {
- BlacklistNodeToTextValue(dataNode, dataValue);
- uint64_t version;
- if (ParseDriverVersion(dataValue, &version))
- aDriverInfo.mDriverVersionMax = version;
+ // We explicitly ignore unknown elements.
}
- // <driverVersionComparator> LESS_THAN_OR_EQUAL </driverVersionComparator>
- if (BlacklistNodeGetChildByName(element, NS_LITERAL_STRING("driverVersionComparator"),
- getter_AddRefs(dataNode))) {
- BlacklistNodeToTextValue(dataNode, dataValue);
- aDriverInfo.mComparisonOp = BlacklistComparatorToComparisonOp(dataValue);
- }
-
- // <model>foo</model>
- if (BlacklistNodeGetChildByName(element, NS_LITERAL_STRING("model"),
- getter_AddRefs(dataNode))) {
- BlacklistNodeToTextValue(dataNode, dataValue);
- aDriverInfo.mModel = dataValue;
- }
- // <product>foo</product>
- if (BlacklistNodeGetChildByName(element, NS_LITERAL_STRING("product"),
- getter_AddRefs(dataNode))) {
- BlacklistNodeToTextValue(dataNode, dataValue);
- aDriverInfo.mProduct = dataValue;
- }
- // <manufacturer>foo</manufacturer>
- if (BlacklistNodeGetChildByName(element, NS_LITERAL_STRING("manufacturer"),
- getter_AddRefs(dataNode))) {
- BlacklistNodeToTextValue(dataNode, dataValue);
- aDriverInfo.mManufacturer = dataValue;
- }
- // <hardware>foo</hardware>
- if (BlacklistNodeGetChildByName(element, NS_LITERAL_STRING("hardware"),
- getter_AddRefs(dataNode))) {
- BlacklistNodeToTextValue(dataNode, dataValue);
- aDriverInfo.mHardware = dataValue;
- }
- if (BlacklistAttrToTextValue(element,
- NS_LITERAL_STRING("blockID"),
- dataValue) && !dataValue.IsEmpty()) {
- nsCString blockIdStr = NS_LITERAL_CSTRING("FEATURE_FAILURE_DL_BLACKLIST_") +
- NS_ConvertUTF16toUTF8(dataValue);
- aDriverInfo.mRuleId = blockIdStr.get();
- } else {
- aDriverInfo.mRuleId = NS_LITERAL_CSTRING("FEATURE_FAILURE_DL_BLACKLIST_NO_ID");
- }
-
- // We explicitly ignore unknown elements.
-
return true;
}
static void
-BlacklistEntriesToDriverInfo(nsIDOMHTMLCollection* aBlacklistEntries,
+BlacklistEntriesToDriverInfo(nsTArray<nsCString>& aBlacklistEntries,
nsTArray<GfxDriverInfo>& aDriverInfo)
{
- uint32_t length;
- if (NS_FAILED(aBlacklistEntries->GetLength(&length)))
- return;
+ aDriverInfo.Clear();
+ aDriverInfo.SetLength(aBlacklistEntries.Length());
- aDriverInfo.Clear();
- aDriverInfo.SetLength(length);
- for (uint32_t i = 0; i < length; ++i) {
- nsCOMPtr<nsIDOMNode> blacklistEntry;
- if (NS_SUCCEEDED(aBlacklistEntries->Item(i,
- getter_AddRefs(blacklistEntry))) &&
- blacklistEntry) {
- GfxDriverInfo di;
- if (BlacklistEntryToDriverInfo(blacklistEntry, di)) {
- aDriverInfo[i] = di;
- // Prevent di falling out of scope from destroying the devices.
- di.mDeleteDevices = false;
- }
+ for (uint32_t i = 0; i < aBlacklistEntries.Length(); ++i) {
+ nsCString blacklistEntry = aBlacklistEntries[i];
+ GfxDriverInfo di;
+ if (BlacklistEntryToDriverInfo(blacklistEntry, di)) {
+ aDriverInfo[i] = di;
+ // Prevent di falling out of scope from destroying the devices.
+ di.mDeleteDevices = false;
}
}
}
NS_IMETHODIMP
GfxInfoBase::Observe(nsISupports* aSubject, const char* aTopic,
const char16_t* aData)
{
if (strcmp(aTopic, "blocklist-data-gfxItems") == 0) {
- nsCOMPtr<nsIDOMElement> gfxItems = do_QueryInterface(aSubject);
- if (gfxItems) {
- nsCOMPtr<nsIDOMHTMLCollection> blacklistEntries;
- if (NS_SUCCEEDED(gfxItems->
- GetElementsByTagName(NS_LITERAL_STRING(BLACKLIST_ENTRY_TAG_NAME),
- getter_AddRefs(blacklistEntries))) &&
- blacklistEntries)
- {
- nsTArray<GfxDriverInfo> driverInfo;
- BlacklistEntriesToDriverInfo(blacklistEntries, driverInfo);
- EvaluateDownloadedBlacklist(driverInfo);
- }
+ nsTArray<GfxDriverInfo> driverInfo;
+ nsTArray<nsCString> blacklistEntries;
+ nsCString utf8Data = NS_ConvertUTF16toUTF8(aData);
+ if (utf8Data.Length() > 0) {
+ ParseString(utf8Data, '\n', blacklistEntries);
}
+ BlacklistEntriesToDriverInfo(blacklistEntries, driverInfo);
+ EvaluateDownloadedBlacklist(driverInfo);
}
return NS_OK;
}
GfxInfoBase::GfxInfoBase()
: mMutex("GfxInfoBase")
{
@@ -765,17 +616,17 @@ GfxInfoBase::FindBlocklistedDeviceInList
OperatingSystem os)
{
int32_t status = nsIGfxInfo::FEATURE_STATUS_UNKNOWN;
uint32_t i = 0;
for (; i < info.Length(); i++) {
// Do the operating system check first, no point in getting the driver
// info if we won't need to use it. If the OS of the system we are running
- // on is unknown, we still let DRIVER_OS_ALL catch and disable it;
+ // on is unknown, we still let DRIVER_OS_ALL catch and disable it;
// if the OS of the downloadable entry is unknown, we skip the entry
// as invalid.
if (info[i].mOperatingSystem == DRIVER_OS_UNKNOWN ||
(info[i].mOperatingSystem != DRIVER_OS_ALL &&
info[i].mOperatingSystem != os))
{
continue;
}