diff options
Diffstat (limited to 'toolkit/mozapps/extensions/nsBlocklistService.js')
-rw-r--r-- | toolkit/mozapps/extensions/nsBlocklistService.js | 519 |
1 files changed, 165 insertions, 354 deletions
diff --git a/toolkit/mozapps/extensions/nsBlocklistService.js b/toolkit/mozapps/extensions/nsBlocklistService.js index 0af90430c..936c9d1b5 100644 --- a/toolkit/mozapps/extensions/nsBlocklistService.js +++ b/toolkit/mozapps/extensions/nsBlocklistService.js @@ -12,29 +12,25 @@ const Cr = Components.results; Components.utils.import("resource://gre/modules/XPCOMUtils.jsm"); Components.utils.import("resource://gre/modules/Services.jsm"); -Components.utils.import("resource://gre/modules/AppConstants.jsm"); try { // AddonManager.jsm doesn't allow itself to be imported in the child // process. We're used in the child process (for now), so guard against // this. Components.utils.import("resource://gre/modules/AddonManager.jsm"); - /* globals AddonManagerPrivate*/ } catch (e) { } XPCOMUtils.defineLazyModuleGetter(this, "FileUtils", "resource://gre/modules/FileUtils.jsm"); -XPCOMUtils.defineLazyModuleGetter(this, "UpdateUtils", - "resource://gre/modules/UpdateUtils.jsm"); +XPCOMUtils.defineLazyModuleGetter(this, "UpdateChannel", + "resource://gre/modules/UpdateChannel.jsm"); XPCOMUtils.defineLazyModuleGetter(this, "OS", "resource://gre/modules/osfile.jsm"); -XPCOMUtils.defineLazyModuleGetter(this, "ServiceRequest", - "resource://gre/modules/ServiceRequest.jsm"); XPCOMUtils.defineLazyModuleGetter(this, "Task", "resource://gre/modules/Task.jsm"); -const TOOLKIT_ID = "toolkit@mozilla.org"; +const TOOLKIT_ID = "toolkit@mozilla.org" const KEY_PROFILEDIR = "ProfD"; const KEY_APPDIR = "XCurProcD"; const FILE_BLOCKLIST = "blocklist.xml"; @@ -47,8 +43,7 @@ const PREF_BLOCKLIST_LEVEL = "extensions.blocklist.level"; const PREF_BLOCKLIST_PINGCOUNTTOTAL = "extensions.blocklist.pingCountTotal"; const PREF_BLOCKLIST_PINGCOUNTVERSION = "extensions.blocklist.pingCountVersion"; const PREF_BLOCKLIST_SUPPRESSUI = "extensions.blocklist.suppressUI"; -const PREF_ONECRL_VIA_AMO = "security.onecrl.via.amo"; -const PREF_BLOCKLIST_UPDATE_ENABLED = "services.blocklist.update_enabled"; +const PREF_PLUGINS_NOTIFYUSER = "plugins.update.notifyUser"; const PREF_GENERAL_USERAGENT_LOCALE = "general.useragent.locale"; const PREF_APP_DISTRIBUTION = "distribution.id"; const PREF_APP_DISTRIBUTION_VERSION = "distribution.version"; @@ -83,30 +78,17 @@ XPCOMUtils.defineLazyServiceGetter(this, "gCertBlocklistService", "@mozilla.org/security/certblocklist;1", "nsICertBlocklist"); -XPCOMUtils.defineLazyGetter(this, "gPref", function() { +XPCOMUtils.defineLazyGetter(this, "gPref", function bls_gPref() { return Cc["@mozilla.org/preferences-service;1"].getService(Ci.nsIPrefService). QueryInterface(Ci.nsIPrefBranch); }); -// From appinfo in Services.jsm. It is not possible to use the one in -// Services.jsm since it will not successfully QueryInterface nsIXULAppInfo in -// xpcshell tests due to other code calling Services.appinfo before the -// nsIXULAppInfo is created by the tests. -XPCOMUtils.defineLazyGetter(this, "gApp", function() { - let appinfo = Cc["@mozilla.org/xre/app-info;1"] - .getService(Ci.nsIXULRuntime); - try { - appinfo.QueryInterface(Ci.nsIXULAppInfo); - } catch (ex) { - // Not all applications implement nsIXULAppInfo (e.g. xpcshell doesn't). - if (!(ex instanceof Components.Exception) || - ex.result != Cr.NS_NOINTERFACE) - throw ex; - } - return appinfo; +XPCOMUtils.defineLazyGetter(this, "gApp", function bls_gApp() { + return Cc["@mozilla.org/xre/app-info;1"].getService(Ci.nsIXULAppInfo). + QueryInterface(Ci.nsIXULRuntime); }); -XPCOMUtils.defineLazyGetter(this, "gABI", function() { +XPCOMUtils.defineLazyGetter(this, "gABI", function bls_gABI() { let abi = null; try { abi = gApp.XPCOMABI; @@ -114,20 +96,19 @@ XPCOMUtils.defineLazyGetter(this, "gABI", function() { catch (e) { LOG("BlockList Global gABI: XPCOM ABI unknown."); } - - if (AppConstants.platform == "macosx") { - // Mac universal build should report a different ABI than either macppc - // or mactel. - let macutils = Cc["@mozilla.org/xpcom/mac-utils;1"]. - getService(Ci.nsIMacUtils); - - if (macutils.isUniversalBinary) - abi += "-u-" + macutils.architecturesInBinary; - } +#ifdef XP_MACOSX + // Mac universal build should report a different ABI than either macppc + // or mactel. + let macutils = Cc["@mozilla.org/xpcom/mac-utils;1"]. + getService(Ci.nsIMacUtils); + + if (macutils.isUniversalBinary) + abi += "-u-" + macutils.architecturesInBinary; +#endif return abi; }); -XPCOMUtils.defineLazyGetter(this, "gOSVersion", function() { +XPCOMUtils.defineLazyGetter(this, "gOSVersion", function bls_gOSVersion() { let osVersion; let sysInfo = Cc["@mozilla.org/system-info;1"]. getService(Ci.nsIPropertyBag2); @@ -151,7 +132,7 @@ XPCOMUtils.defineLazyGetter(this, "gOSVersion", function() { }); // shared code for suppressing bad cert dialogs -XPCOMUtils.defineLazyGetter(this, "gCertUtils", function() { +XPCOMUtils.defineLazyGetter(this, "gCertUtils", function bls_gCertUtils() { let temp = { }; Components.utils.import("resource://gre/modules/CertUtils.jsm", temp); return temp; @@ -289,6 +270,26 @@ function parseRegExp(aStr) { } /** + * Helper function to test if the blockEntry matches with the plugin. + * + * @param blockEntry + * The plugin blocklist entries to compare against. + * @param plugin + * The nsIPluginTag to get the blocklist state for. + * @returns True if the blockEntry matches the plugin, false otherwise. + */ +function matchesAllPluginNames(blockEntry, plugin) { + for (let name in blockEntry.matches) { + if (!(name in plugin) || + typeof(plugin[name]) != "string" || + !blockEntry.matches[name].test(plugin[name])) { + return false; + } + } + return true; +} + +/** * Manages the Blocklist. The Blocklist is a representation of the contents of * blocklist.xml and allows us to remotely disable / re-enable blocklisted * items managed by the Extension Manager with an item's appDisabled property. @@ -305,9 +306,6 @@ function Blocklist() { gPref.addObserver("extensions.blocklist.", this, false); gPref.addObserver(PREF_EM_LOGGING_ENABLED, this, false); this.wrappedJSObject = this; - // requests from child processes come in here, see receiveMessage. - Services.ppmm.addMessageListener("Blocklist:getPluginBlocklistState", this); - Services.ppmm.addMessageListener("Blocklist:content-blocklist-updated", this); } Blocklist.prototype = { @@ -327,21 +325,14 @@ Blocklist.prototype = { * (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); - }, - - observe: function(aSubject, aTopic, aData) { + observe: function Blocklist_observe(aSubject, aTopic, aData) { switch (aTopic) { case "xpcom-shutdown": - this.shutdown(); + Services.obs.removeObserver(this, "xpcom-shutdown"); + gPref.removeObserver("extensions.blocklist.", this); + gPref.removeObserver(PREF_EM_LOGGING_ENABLED, this); break; case "nsPref:changed": switch (aData) { @@ -367,30 +358,14 @@ Blocklist.prototype = { } }, - // Message manager message handlers - receiveMessage: function(aMsg) { - switch (aMsg.name) { - case "Blocklist:getPluginBlocklistState": - return this.getPluginBlocklistState(aMsg.data.addonData, - aMsg.data.appVersion, - aMsg.data.toolkitVersion); - case "Blocklist:content-blocklist-updated": - Services.obs.notifyObservers(null, "content-blocklist-updated", null); - break; - default: - throw new Error("Unknown blocklist message received from content: " + aMsg.name); - } - return undefined; - }, - /* See nsIBlocklistService */ - isAddonBlocklisted: function(addon, appVersion, toolkitVersion) { + isAddonBlocklisted: function Blocklist_isAddonBlocklisted(addon, appVersion, toolkitVersion) { return this.getAddonBlocklistState(addon, appVersion, toolkitVersion) == Ci.nsIBlocklistService.STATE_BLOCKED; }, /* See nsIBlocklistService */ - getAddonBlocklistState: function(addon, appVersion, toolkitVersion) { + getAddonBlocklistState: function Blocklist_getAddonBlocklistState(addon, appVersion, toolkitVersion) { if (!this._isBlocklistLoaded()) this._loadBlocklist(); return this._getAddonBlocklistState(addon, this._addonEntries, @@ -416,14 +391,11 @@ Blocklist.prototype = { * @returns The blocklist state for the item, one of the STATE constants as * defined in nsIBlocklistService. */ - _getAddonBlocklistState: function(addon, addonEntries, appVersion, toolkitVersion) { + _getAddonBlocklistState: function Blocklist_getAddonBlocklistStateCall(addon, + addonEntries, appVersion, toolkitVersion) { if (!gBlocklistEnabled) return Ci.nsIBlocklistService.STATE_NOT_BLOCKED; - // Not all applications implement nsIXULAppInfo (e.g. xpcshell doesn't). - if (!appVersion && !gApp.version) - return Ci.nsIBlocklistService.STATE_NOT_BLOCKED; - if (!appVersion) appVersion = gApp.version; if (!toolkitVersion) @@ -447,12 +419,13 @@ Blocklist.prototype = { * @param addon * The add-on whose to-be-reset prefs are to be found. */ - _getAddonPrefs: function(addon) { + _getAddonPrefs: function Blocklist_getAddonPrefs(addon) { let entry = this._findMatchingAddonEntry(this._addonEntries, addon); return entry.prefs.slice(0); }, - _findMatchingAddonEntry: function(aAddonEntries, aAddon) { + _findMatchingAddonEntry: function Blocklist_findMatchingAddonEntry(aAddonEntries, + aAddon) { if (!aAddon) return null; // Returns true if the params object passes the constraints set by entry. @@ -492,7 +465,7 @@ Blocklist.prototype = { }, /* See nsIBlocklistService */ - getAddonBlocklistURL: function(addon, appVersion, toolkitVersion) { + getAddonBlocklistURL: function Blocklist_getAddonBlocklistURL(addon, appVersion, toolkitVersion) { if (!gBlocklistEnabled) return ""; @@ -506,14 +479,14 @@ Blocklist.prototype = { return this._createBlocklistURL(blItem.blockID); }, - _createBlocklistURL: function(id) { + _createBlocklistURL: function Blocklist_createBlocklistURL(id) { let url = Services.urlFormatter.formatURLPref(PREF_BLOCKLIST_ITEM_URL); url = url.replace(/%blockID%/g, id); return url; }, - notify: function(aTimer) { + notify: function Blocklist_notify(aTimer) { if (!gBlocklistEnabled) return; @@ -556,18 +529,14 @@ Blocklist.prototype = { pingCountTotal = 1; dsURI = dsURI.replace(/%APP_ID%/g, gApp.ID); - // Not all applications implement nsIXULAppInfo (e.g. xpcshell doesn't). - if (gApp.version) - dsURI = dsURI.replace(/%APP_VERSION%/g, gApp.version); + dsURI = dsURI.replace(/%APP_VERSION%/g, gApp.version); dsURI = dsURI.replace(/%PRODUCT%/g, gApp.name); - // Not all applications implement nsIXULAppInfo (e.g. xpcshell doesn't). - if (gApp.version) - dsURI = dsURI.replace(/%VERSION%/g, gApp.version); + dsURI = dsURI.replace(/%VERSION%/g, gApp.version); dsURI = dsURI.replace(/%BUILD_ID%/g, gApp.appBuildID); dsURI = dsURI.replace(/%BUILD_TARGET%/g, gApp.OS + "_" + gABI); dsURI = dsURI.replace(/%OS_VERSION%/g, gOSVersion); dsURI = dsURI.replace(/%LOCALE%/g, getLocale()); - dsURI = dsURI.replace(/%CHANNEL%/g, UpdateUtils.UpdateChannel); + dsURI = dsURI.replace(/%CHANNEL%/g, UpdateChannel.get()); dsURI = dsURI.replace(/%PLATFORM_VERSION%/g, gApp.platformVersion); dsURI = dsURI.replace(/%DISTRIBUTION%/g, getDistributionPrefValue(PREF_APP_DISTRIBUTION)); @@ -612,15 +581,19 @@ Blocklist.prototype = { } LOG("Blocklist::notify: Requesting " + uri.spec); - let request = new ServiceRequest(); + var request = Cc["@mozilla.org/xmlextras/xmlhttprequest;1"]. + createInstance(Ci.nsIXMLHttpRequest); request.open("GET", uri.spec, true); request.channel.notificationCallbacks = new gCertUtils.BadCertHandler(); request.overrideMimeType("text/xml"); request.setRequestHeader("Cache-Control", "no-cache"); request.QueryInterface(Components.interfaces.nsIJSXMLHttpRequest); - request.addEventListener("error", event => this.onXMLError(event), false); - request.addEventListener("load", event => this.onXMLLoad(event), false); + var self = this; + request.addEventListener("error", function errorEventListener(event) { + self.onXMLError(event); }, false); + request.addEventListener("load", function loadEventListener(event) { + self.onXMLLoad(event); }, false); request.send(null); // When the blocklist loads we need to compare it to the current copy so @@ -629,7 +602,7 @@ Blocklist.prototype = { this._loadBlocklist(); }, - onXMLLoad: Task.async(function*(aEvent) { + onXMLLoad: Task.async(function* (aEvent) { let request = aEvent.target; try { gCertUtils.checkCert(request.channel); @@ -648,12 +621,9 @@ Blocklist.prototype = { 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 { @@ -664,7 +634,7 @@ Blocklist.prototype = { } }), - onXMLError: function(aEvent) { + onXMLError: function Blocklist_onXMLError(aEvent) { try { var request = aEvent.target; // the following may throw (e.g. a local file or timeout) @@ -690,9 +660,8 @@ Blocklist.prototype = { * Finds the newest blocklist file from the application and the profile and * load it or does nothing if neither exist. */ - _loadBlocklist: function() { + _loadBlocklist: function Blocklist_loadBlocklist() { this._addonEntries = []; - this._gfxEntries = []; this._pluginEntries = []; var profFile = FileUtils.getFile(KEY_PROFILEDIR, [FILE_BLOCKLIST]); if (profFile.exists()) { @@ -718,7 +687,7 @@ Blocklist.prototype = { # <pref>accessibility.blockautorefresh</pref> # </prefs> # <versionRange minVersion="1.0" maxVersion="2.0.*"> -# <targetApplication id="{ec8030f7-c20a-464f-9b0e-13a3a9e97384}"> +# <targetApplication id="{8de7fcbb-c55c-4fbe-bfc5-fc555c87dbc4}"> # <versionRange minVersion="1.5" maxVersion="1.5.*"/> # <versionRange minVersion="1.7" maxVersion="1.7.*"/> # </targetApplication> @@ -727,7 +696,7 @@ Blocklist.prototype = { # </targetApplication> # </versionRange> # <versionRange minVersion="3.0" maxVersion="3.0.*"> -# <targetApplication id="{ec8030f7-c20a-464f-9b0e-13a3a9e97384}"> +# <targetApplication id="{8de7fcbb-c55c-4fbe-bfc5-fc555c87dbc4}"> # <versionRange minVersion="1.5" maxVersion="1.5.*"/> # </targetApplication> # <targetApplication id="toolkit@mozilla.org"> @@ -740,7 +709,7 @@ Blocklist.prototype = { # </emItem> # <emItem id="item_3@domain"> # <versionRange> -# <targetApplication id="{ec8030f7-c20a-464f-9b0e-13a3a9e97384}"> +# <targetApplication id="{8de7fcbb-c55c-4fbe-bfc5-fc555c87dbc4}"> # <versionRange minVersion="1.5" maxVersion="1.5.*"/> # </targetApplication> # </versionRange> @@ -766,14 +735,11 @@ Blocklist.prototype = { # <!-- ... as is the serial number DER data --> # <serialNumber>AkHVNA==</serialNumber> # </certItem> -# <!-- subject is the DER subject name data base64 encoded... --> -# <certItem subject="MA0xCzAJBgNVBAMMAmNh" pubKeyHash="/xeHA5s+i9/z9d8qy6JEuE1xGoRYIwgJuTE/lmaGJ7M="> -# </certItem> # </certItems> # </blocklist> */ - _loadBlocklistFromFile: function(file) { + _loadBlocklistFromFile: function Blocklist_loadBlocklistFromFile(file) { if (!gBlocklistEnabled) { LOG("Blocklist::_loadBlocklistFromFile: blocklist is disabled"); return; @@ -829,7 +795,7 @@ Blocklist.prototype = { }, _isBlocklistLoaded: function() { - return this._addonEntries != null && this._gfxEntries != null && this._pluginEntries != null; + return this._addonEntries != null && this._pluginEntries != null; }, _isBlocklistPreloaded: function() { @@ -839,7 +805,6 @@ Blocklist.prototype = { /* Used for testing */ _clear: function() { this._addonEntries = null; - this._gfxEntries = null; this._pluginEntries = null; this._preloadedBlocklistContent = null; }, @@ -854,7 +819,7 @@ Blocklist.prototype = { } var appFile = FileUtils.getFile(KEY_APPDIR, [FILE_BLOCKLIST]); - try { + try{ yield this._preloadBlocklistFile(appFile.path); return; } catch (e) { @@ -864,7 +829,7 @@ Blocklist.prototype = { LOG("Blocklist::_preloadBlocklist: no XML File found"); }), - _preloadBlocklistFile: Task.async(function*(path) { + _preloadBlocklistFile: Task.async(function* (path){ if (this._addonEntries) { // The file has been already loaded. return; @@ -883,7 +848,7 @@ Blocklist.prototype = { } }), - _loadBlocklistFromString : function(text) { + _loadBlocklistFromString : function Blocklist_loadBlocklistFromString(text) { try { var parser = Cc["@mozilla.org/xmlextras/domparser;1"]. createInstance(Ci.nsIDOMParser); @@ -895,46 +860,30 @@ Blocklist.prototype = { return; } - var populateCertBlocklist = getPref("getBoolPref", PREF_ONECRL_VIA_AMO, true); - var childNodes = doc.documentElement.childNodes; for (let element of childNodes) { if (!(element instanceof Ci.nsIDOMElement)) continue; switch (element.localName) { case "emItems": - this._addonEntries = this._processItemNodes(element.childNodes, "emItem", + this._addonEntries = this._processItemNodes(element.childNodes, "em", this._handleEmItemNode); break; case "pluginItems": - // We don't support plugins on b2g. - if (AppConstants.MOZ_B2G) { - return; - } - this._pluginEntries = this._processItemNodes(element.childNodes, "pluginItem", + this._pluginEntries = this._processItemNodes(element.childNodes, "plugin", this._handlePluginItemNode); break; case "certItems": - if (populateCertBlocklist) { - 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); + this._processItemNodes(element.childNodes, "cert", + this._handleCertItemNode.bind(this)); break; default: - LOG("Blocklist::_loadBlocklistFromString: ignored entries " + element.localName); + Services.obs.notifyObservers(element, + "blocklist-data-" + element.localName, + null); } } - if (populateCertBlocklist) { - gCertBlocklistService.saveEntries(); - } - if (this._gfxEntries.length > 0) { - this._notifyObserversBlocklistGFX(); - } + gCertBlocklistService.saveEntries(); } catch (e) { LOG("Blocklist::_loadBlocklistFromFile: Error constructing blocklist " + e); @@ -942,8 +891,9 @@ Blocklist.prototype = { } }, - _processItemNodes: function(itemNodes, itemName, handler) { + _processItemNodes: function Blocklist_processItemNodes(itemNodes, prefix, 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) || @@ -955,34 +905,21 @@ Blocklist.prototype = { return result; }, - _handleCertItemNode: function(blocklistElement, result) { + _handleCertItemNode: function Blocklist_handleCertItemNode(blocklistElement, + result) { let issuer = blocklistElement.getAttribute("issuerName"); - if (issuer) { - for (let snElement of blocklistElement.children) { - try { - gCertBlocklistService.revokeCertByIssuerAndSerial(issuer, snElement.textContent); - } catch (e) { - // we want to keep trying other elements since missing all items - // is worse than missing one - LOG("Blocklist::_handleCertItemNode: Error adding revoked cert by Issuer and Serial" + e); - } - } - return; - } - - let pubKeyHash = blocklistElement.getAttribute("pubKeyHash"); - let subject = blocklistElement.getAttribute("subject"); - - if (pubKeyHash && subject) { + for (let snElement of blocklistElement.children) { try { - gCertBlocklistService.revokeCertBySubjectAndPubKey(subject, pubKeyHash); + gCertBlocklistService.addRevokedCert(issuer, snElement.textContent); } catch (e) { - LOG("Blocklist::_handleCertItemNode: Error adding revoked cert by Subject and PubKey" + e); + // we want to keep trying other elements since missing all items + // is worse than missing one + LOG("Blocklist::_handleCertItemNode: Error adding revoked cert " + e); } } }, - _handleEmItemNode: function(blocklistElement, result) { + _handleEmItemNode: function Blocklist_handleEmItemNode(blocklistElement, result) { if (!matchesOSABI(blocklistElement)) return; @@ -1035,7 +972,7 @@ Blocklist.prototype = { result.push(blockEntry); }, - _handlePluginItemNode: function(blocklistElement, result) { + _handlePluginItemNode: function Blocklist_handlePluginItemNode(blocklistElement, result) { if (!matchesOSABI(blocklistElement)) return; @@ -1080,81 +1017,9 @@ Blocklist.prototype = { 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; - } + getPluginBlocklistState: function Blocklist_getPluginBlocklistState(plugin, + appVersion, toolkitVersion) { if (!this._isBlocklistLoaded()) this._loadBlocklist(); return this._getPluginBlocklistState(plugin, this._pluginEntries, @@ -1162,8 +1027,8 @@ Blocklist.prototype = { }, /** - * Private helper to get the blocklist entry for a plugin given a set of - * blocklist entries and versions. + * Private version of getPluginBlocklistState that allows the caller to pass in + * the plugin blocklist entries. * * @param plugin * The nsIPluginTag to get the blocklist state for. @@ -1175,15 +1040,12 @@ Blocklist.prototype = { * @param toolkitVersion * The toolkit version to compare to, will use the current version if * null. - * @returns {entry: blocklistEntry, version: blocklistEntryVersion}, - * or null if there is no matching entry. + * @returns The blocklist state for the item, one of the STATE constants as + * defined in nsIBlocklistService. */ - _getPluginBlocklistEntry: function(plugin, pluginEntries, appVersion, toolkitVersion) { + _getPluginBlocklistState: function Blocklist_getPluginBlocklistState(plugin, + pluginEntries, appVersion, toolkitVersion) { if (!gBlocklistEnabled) - return null; - - // Not all applications implement nsIXULAppInfo (e.g. xpcshell doesn't). - if (!appVersion && !gApp.version) return Ci.nsIBlocklistService.STATE_NOT_BLOCKED; if (!appVersion) @@ -1191,7 +1053,7 @@ Blocklist.prototype = { if (!toolkitVersion) toolkitVersion = gApp.platformVersion; - for (var blockEntry of pluginEntries) { + for each (var blockEntry in pluginEntries) { var matchFailed = false; for (var name in blockEntry.matches) { if (!(name in plugin) || @@ -1207,66 +1069,52 @@ Blocklist.prototype = { for (let blockEntryVersion of blockEntry.versions) { if (blockEntryVersion.includesItem(plugin.version, appVersion, - toolkitVersion)) { - return {entry: blockEntry, version: blockEntryVersion}; + toolkitVersion)) { + if (blockEntryVersion.severity >= gBlocklistLevel) + return Ci.nsIBlocklistService.STATE_BLOCKED; + if (blockEntryVersion.severity == SEVERITY_OUTDATED) { + let vulnerabilityStatus = blockEntryVersion.vulnerabilityStatus; + if (vulnerabilityStatus == VULNERABILITYSTATUS_UPDATE_AVAILABLE) + return Ci.nsIBlocklistService.STATE_VULNERABLE_UPDATE_AVAILABLE; + if (vulnerabilityStatus == VULNERABILITYSTATUS_NO_UPDATE) + return Ci.nsIBlocklistService.STATE_VULNERABLE_NO_UPDATE; + return Ci.nsIBlocklistService.STATE_OUTDATED; + } + return Ci.nsIBlocklistService.STATE_SOFTBLOCKED; } } } - return null; + return Ci.nsIBlocklistService.STATE_NOT_BLOCKED; }, /** - * Private version of getPluginBlocklistState that allows the caller to pass in - * the plugin blocklist entries. - * - * @param plugin - * The nsIPluginTag to get the blocklist state for. - * @param pluginEntries - * The plugin blocklist entries to compare against. - * @param appVersion - * The application version to compare to, will use the current - * version if null. - * @param toolkitVersion - * The toolkit version to compare to, will use the current version if - * null. - * @returns The blocklist state for the item, one of the STATE constants as - * defined in nsIBlocklistService. + * Get the matching blocklist entry for the passed plugin, if + * available. + * @param plugin The plugin to find the block entry for. + * @returns The block entry which matches the passed plugin, null + * otherwise. */ - _getPluginBlocklistState: function(plugin, pluginEntries, appVersion, toolkitVersion) { + _getPluginBlockEntry: function (plugin) { + if (!gBlocklistEnabled) + return null; - let r = this._getPluginBlocklistEntry(plugin, pluginEntries, - appVersion, toolkitVersion); - if (!r) { - return Ci.nsIBlocklistService.STATE_NOT_BLOCKED; - } + if (!this._isBlocklistLoaded()) + this._loadBlocklist(); - let {entry: blockEntry, version: blockEntryVersion} = r; - - if (blockEntryVersion.severity >= gBlocklistLevel) - return Ci.nsIBlocklistService.STATE_BLOCKED; - if (blockEntryVersion.severity == SEVERITY_OUTDATED) { - let vulnerabilityStatus = blockEntryVersion.vulnerabilityStatus; - if (vulnerabilityStatus == VULNERABILITYSTATUS_UPDATE_AVAILABLE) - return Ci.nsIBlocklistService.STATE_VULNERABLE_UPDATE_AVAILABLE; - if (vulnerabilityStatus == VULNERABILITYSTATUS_NO_UPDATE) - return Ci.nsIBlocklistService.STATE_VULNERABLE_NO_UPDATE; - return Ci.nsIBlocklistService.STATE_OUTDATED; + for each (let blockEntry in this._pluginEntries) { + if (matchesAllPluginNames(blockEntry, plugin)) { + return blockEntry; + } } - return Ci.nsIBlocklistService.STATE_SOFTBLOCKED; + + return null; }, /* See nsIBlocklistService */ - getPluginBlocklistURL: function(plugin) { - if (!this._isBlocklistLoaded()) - this._loadBlocklist(); - - let r = this._getPluginBlocklistEntry(plugin, this._pluginEntries); - if (!r) { - return null; - } - let {entry: blockEntry, version: blockEntryVersion} = r; - if (!blockEntry.blockID) { + getPluginBlocklistURL: function Blocklist_getPluginBlocklistURL(plugin) { + let blockEntry = this._getPluginBlockEntry(plugin); + if (!blockEntry || !blockEntry.blockID) { return null; } @@ -1274,50 +1122,16 @@ Blocklist.prototype = { }, /* See nsIBlocklistService */ - getPluginInfoURL: function(plugin) { - if (!this._isBlocklistLoaded()) - this._loadBlocklist(); - - let r = this._getPluginBlocklistEntry(plugin, this._pluginEntries); - if (!r) { - return null; - } - let {entry: blockEntry, version: blockEntryVersion} = r; - if (!blockEntry.blockID) { + getPluginInfoURL: function (plugin) { + let blockEntry = this._getPluginBlockEntry(plugin); + if (!blockEntry || !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; - } - + _blocklistUpdated: function Blocklist_blocklistUpdated(oldAddonEntries, oldPluginEntries) { var addonList = []; // A helper function that reverts the prefs passed to default values. @@ -1325,13 +1139,15 @@ Blocklist.prototype = { for (let pref of prefs) gPref.clearUserPref(pref); } + var self = this; const types = ["extension", "theme", "locale", "dictionary", "service"]; - AddonManager.getAddonsByTypes(types, addons => { + AddonManager.getAddonsByTypes(types, function blocklistUpdated_getAddonsByTypes(addons) { + for (let addon of addons) { let oldState = Ci.nsIBlocklistService.STATE_NOTBLOCKED; if (oldAddonEntries) - oldState = this._getAddonBlocklistState(addon, oldAddonEntries); - let state = this.getAddonBlocklistState(addon); + oldState = self._getAddonBlocklistState(addon, oldAddonEntries); + let state = self.getAddonBlocklistState(addon); LOG("Blocklist state for " + addon.id + " changed from " + oldState + " to " + state); @@ -1342,7 +1158,7 @@ Blocklist.prototype = { if (state === Ci.nsIBlocklistService.STATE_BLOCKED) { // It's a hard block. We must reset certain preferences. - let prefs = this._getAddonPrefs(addon); + let prefs = self._getAddonPrefs(addon); resetPrefs(prefs); } @@ -1364,15 +1180,8 @@ Blocklist.prototype = { // If the add-on is already disabled for some reason then don't warn // about it - if (!addon.isActive) { - // But mark it as softblocked if necessary. Note that we avoid setting - // softDisabled at the same time as userDisabled to make it clear - // which was the original cause of the add-on becoming disabled in a - // way that the user can change. - if (state == Ci.nsIBlocklistService.STATE_SOFTBLOCKED && !addon.userDisabled) - addon.softDisabled = true; + if (!addon.isActive) continue; - } addonList.push({ name: addon.name, @@ -1381,7 +1190,7 @@ Blocklist.prototype = { disable: false, blocked: state == Ci.nsIBlocklistService.STATE_BLOCKED, item: addon, - url: this.getAddonBlocklistURL(addon), + url: self.getAddonBlocklistURL(addon), }); } @@ -1394,8 +1203,8 @@ Blocklist.prototype = { for (let plugin of plugins) { let oldState = -1; if (oldPluginEntries) - oldState = this._getPluginBlocklistState(plugin, oldPluginEntries); - let state = this.getPluginBlocklistState(plugin); + oldState = self._getPluginBlocklistState(plugin, oldPluginEntries); + let state = self.getPluginBlocklistState(plugin); LOG("Blocklist state for " + plugin.name + " changed from " + oldState + " to " + state); // We don't want to re-warn about items @@ -1407,9 +1216,11 @@ Blocklist.prototype = { plugin.enabledState = Ci.nsIPluginTag.STATE_DISABLED; } else if (!plugin.disabled && state != Ci.nsIBlocklistService.STATE_NOT_BLOCKED) { - if (state != Ci.nsIBlocklistService.STATE_OUTDATED && - state != Ci.nsIBlocklistService.STATE_VULNERABLE_UPDATE_AVAILABLE && - state != Ci.nsIBlocklistService.STATE_VULNERABLE_NO_UPDATE) { + if (state == Ci.nsIBlocklistService.STATE_OUTDATED) { + gPref.setBoolPref(PREF_PLUGINS_NOTIFYUSER, true); + } + else if (state != Ci.nsIBlocklistService.STATE_VULNERABLE_UPDATE_AVAILABLE && + state != Ci.nsIBlocklistService.STATE_VULNERABLE_NO_UPDATE) { addonList.push({ name: plugin.name, version: plugin.version, @@ -1417,14 +1228,14 @@ Blocklist.prototype = { disable: false, blocked: state == Ci.nsIBlocklistService.STATE_BLOCKED, item: plugin, - url: this.getPluginBlocklistURL(plugin), + url: self.getPluginBlocklistURL(plugin), }); } } } if (addonList.length == 0) { - this._notifyObserversBlocklistUpdated(); + Services.obs.notifyObservers(self, "blocklist-updated", ""); return; } @@ -1436,7 +1247,7 @@ Blocklist.prototype = { } catch (e) { LOG(e); } - this._notifyObserversBlocklistUpdated(); + Services.obs.notifyObservers(self, "blocklist-updated", ""); return; } @@ -1451,7 +1262,7 @@ Blocklist.prototype = { Some tests run without UI, so the async code listens to a message that can be sent programatically */ - let applyBlocklistChanges = () => { + let applyBlocklistChanges = function blocklistUpdated_applyBlocklistChanges() { for (let addon of addonList) { if (!addon.disable) continue; @@ -1462,7 +1273,7 @@ Blocklist.prototype = { // This add-on is softblocked. addon.item.softDisabled = true; // We must revert certain prefs. - let prefs = this._getAddonPrefs(addon.item); + let prefs = self._getAddonPrefs(addon.item); resetPrefs(prefs); } } @@ -1470,7 +1281,7 @@ Blocklist.prototype = { if (args.restart) restartApp(); - this._notifyObserversBlocklistUpdated(); + Services.obs.notifyObservers(self, "blocklist-updated", ""); Services.obs.removeObserver(applyBlocklistChanges, "addon-blocklist-closed"); } @@ -1552,7 +1363,7 @@ BlocklistItemData.prototype = { * @returns True if the version range covers the item version and application * or toolkit version. */ - includesItem: function(version, appVersion, toolkitVersion) { + includesItem: function BlocklistItemData_includesItem(version, appVersion, toolkitVersion) { // Some platforms have no version for plugins, these don't match if there // was a min/maxVersion provided if (!version && (this.minVersion || this.maxVersion)) @@ -1582,7 +1393,7 @@ BlocklistItemData.prototype = { * The maximum version. If null it is assumed that version is always * smaller. */ - matchesRange: function(version, minVersion, maxVersion) { + matchesRange: function BlocklistItemData_matchesRange(version, minVersion, maxVersion) { if (minVersion && gVersionChecker.compare(version, minVersion) < 0) return false; if (maxVersion && gVersionChecker.compare(version, maxVersion) > 0) @@ -1599,7 +1410,7 @@ BlocklistItemData.prototype = { * The version of the application to test for. * @returns True if this version range covers the application version given. */ - matchesTargetRange: function(appID, appVersion) { + matchesTargetRange: function BlocklistItemData_matchesTargetRange(appID, appVersion) { var blTargetApp = this.targetApps[appID]; if (!blTargetApp) return false; @@ -1621,7 +1432,7 @@ BlocklistItemData.prototype = { * "minVersion" The minimum version in a version range (default = null). * "maxVersion" The maximum version in a version range (default = null). */ - getBlocklistAppVersions: function(targetAppElement) { + getBlocklistAppVersions: function BlocklistItemData_getBlocklistAppVersions(targetAppElement) { var appVersions = [ ]; if (targetAppElement) { @@ -1649,7 +1460,7 @@ BlocklistItemData.prototype = { * "minVersion" The minimum version in a version range (default = null). * "maxVersion" The maximum version in a version range (default = null). */ - getBlocklistVersionRange: function(versionRangeElement) { + getBlocklistVersionRange: function BlocklistItemData_getBlocklistVersionRange(versionRangeElement) { var minVersion = null; var maxVersion = null; if (!versionRangeElement) |