toolkit/mozapps/extensions/nsBlocklistService.js

Wed, 31 Dec 2014 06:09:35 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Wed, 31 Dec 2014 06:09:35 +0100
changeset 0
6474c204b198
permissions
-rw-r--r--

Cloned upstream origin tor-browser at tor-browser-31.3.0esr-4.5-1-build1
revision ID fc1c9ff7c1b2defdbc039f12214767608f46423f for hacking purpose.

     1 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
     3 /* This Source Code Form is subject to the terms of the Mozilla Public
     4  * License, v. 2.0. If a copy of the MPL was not distributed with this
     5  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
     7 "use strict";
     9 const Cc = Components.classes;
    10 const Ci = Components.interfaces;
    11 const Cr = Components.results;
    13 Components.utils.import("resource://gre/modules/XPCOMUtils.jsm");
    14 Components.utils.import("resource://gre/modules/AddonManager.jsm");
    15 Components.utils.import("resource://gre/modules/Services.jsm");
    17 XPCOMUtils.defineLazyModuleGetter(this, "FileUtils",
    18                                   "resource://gre/modules/FileUtils.jsm");
    19 XPCOMUtils.defineLazyModuleGetter(this, "UpdateChannel",
    20                                   "resource://gre/modules/UpdateChannel.jsm");
    21 XPCOMUtils.defineLazyModuleGetter(this, "OS",
    22                                   "resource://gre/modules/osfile.jsm");
    23 XPCOMUtils.defineLazyModuleGetter(this, "Task",
    24                                   "resource://gre/modules/Task.jsm");
    26 const TOOLKIT_ID                      = "toolkit@mozilla.org"
    27 const KEY_PROFILEDIR                  = "ProfD";
    28 const KEY_APPDIR                      = "XCurProcD";
    29 const FILE_BLOCKLIST                  = "blocklist.xml";
    30 const PREF_BLOCKLIST_LASTUPDATETIME   = "app.update.lastUpdateTime.blocklist-background-update-timer";
    31 const PREF_BLOCKLIST_URL              = "extensions.blocklist.url";
    32 const PREF_BLOCKLIST_ITEM_URL         = "extensions.blocklist.itemURL";
    33 const PREF_BLOCKLIST_ENABLED          = "extensions.blocklist.enabled";
    34 const PREF_BLOCKLIST_INTERVAL         = "extensions.blocklist.interval";
    35 const PREF_BLOCKLIST_LEVEL            = "extensions.blocklist.level";
    36 const PREF_BLOCKLIST_PINGCOUNTTOTAL   = "extensions.blocklist.pingCountTotal";
    37 const PREF_BLOCKLIST_PINGCOUNTVERSION = "extensions.blocklist.pingCountVersion";
    38 const PREF_BLOCKLIST_SUPPRESSUI       = "extensions.blocklist.suppressUI";
    39 const PREF_PLUGINS_NOTIFYUSER         = "plugins.update.notifyUser";
    40 const PREF_GENERAL_USERAGENT_LOCALE   = "general.useragent.locale";
    41 const PREF_APP_DISTRIBUTION           = "distribution.id";
    42 const PREF_APP_DISTRIBUTION_VERSION   = "distribution.version";
    43 const PREF_EM_LOGGING_ENABLED         = "extensions.logging.enabled";
    44 const XMLURI_BLOCKLIST                = "http://www.mozilla.org/2006/addons-blocklist";
    45 const XMLURI_PARSE_ERROR              = "http://www.mozilla.org/newlayout/xml/parsererror.xml"
    46 const UNKNOWN_XPCOM_ABI               = "unknownABI";
    47 const URI_BLOCKLIST_DIALOG            = "chrome://mozapps/content/extensions/blocklist.xul"
    48 const DEFAULT_SEVERITY                = 3;
    49 const DEFAULT_LEVEL                   = 2;
    50 const MAX_BLOCK_LEVEL                 = 3;
    51 const SEVERITY_OUTDATED               = 0;
    52 const VULNERABILITYSTATUS_NONE             = 0;
    53 const VULNERABILITYSTATUS_UPDATE_AVAILABLE = 1;
    54 const VULNERABILITYSTATUS_NO_UPDATE        = 2;
    56 const EXTENSION_BLOCK_FILTERS = ["id", "name", "creator", "homepageURL", "updateURL"];
    58 var gLoggingEnabled = null;
    59 var gBlocklistEnabled = true;
    60 var gBlocklistLevel = DEFAULT_LEVEL;
    62 XPCOMUtils.defineLazyServiceGetter(this, "gConsole",
    63                                    "@mozilla.org/consoleservice;1",
    64                                    "nsIConsoleService");
    66 XPCOMUtils.defineLazyServiceGetter(this, "gVersionChecker",
    67                                    "@mozilla.org/xpcom/version-comparator;1",
    68                                    "nsIVersionComparator");
    70 XPCOMUtils.defineLazyGetter(this, "gPref", function bls_gPref() {
    71   return Cc["@mozilla.org/preferences-service;1"].getService(Ci.nsIPrefService).
    72          QueryInterface(Ci.nsIPrefBranch);
    73 });
    75 XPCOMUtils.defineLazyGetter(this, "gApp", function bls_gApp() {
    76   return Cc["@mozilla.org/xre/app-info;1"].getService(Ci.nsIXULAppInfo).
    77          QueryInterface(Ci.nsIXULRuntime);
    78 });
    80 XPCOMUtils.defineLazyGetter(this, "gABI", function bls_gABI() {
    81   let abi = null;
    82   try {
    83     abi = gApp.XPCOMABI;
    84   }
    85   catch (e) {
    86     LOG("BlockList Global gABI: XPCOM ABI unknown.");
    87   }
    88 #ifdef XP_MACOSX
    89   // Mac universal build should report a different ABI than either macppc
    90   // or mactel.
    91   let macutils = Cc["@mozilla.org/xpcom/mac-utils;1"].
    92                  getService(Ci.nsIMacUtils);
    94   if (macutils.isUniversalBinary)
    95     abi += "-u-" + macutils.architecturesInBinary;
    96 #endif
    97   return abi;
    98 });
   100 XPCOMUtils.defineLazyGetter(this, "gOSVersion", function bls_gOSVersion() {
   101   let osVersion;
   102   let sysInfo = Cc["@mozilla.org/system-info;1"].
   103                 getService(Ci.nsIPropertyBag2);
   104   try {
   105     osVersion = sysInfo.getProperty("name") + " " + sysInfo.getProperty("version");
   106   }
   107   catch (e) {
   108     LOG("BlockList Global gOSVersion: OS Version unknown.");
   109   }
   111   if (osVersion) {
   112     try {
   113       osVersion += " (" + sysInfo.getProperty("secondaryLibrary") + ")";
   114     }
   115     catch (e) {
   116       // Not all platforms have a secondary widget library, so an error is nothing to worry about.
   117     }
   118     osVersion = encodeURIComponent(osVersion);
   119   }
   120   return osVersion;
   121 });
   123 // shared code for suppressing bad cert dialogs
   124 XPCOMUtils.defineLazyGetter(this, "gCertUtils", function bls_gCertUtils() {
   125   let temp = { };
   126   Components.utils.import("resource://gre/modules/CertUtils.jsm", temp);
   127   return temp;
   128 });
   130 function getObserverService() {
   131   return Cc["@mozilla.org/observer-service;1"].getService(Ci.nsIObserverService);
   132 }
   134 /**
   135  * Logs a string to the error console.
   136  * @param   string
   137  *          The string to write to the error console..
   138  */
   139 function LOG(string) {
   140   if (gLoggingEnabled) {
   141     dump("*** " + string + "\n");
   142     gConsole.logStringMessage(string);
   143   }
   144 }
   146 /**
   147  * Gets a preference value, handling the case where there is no default.
   148  * @param   func
   149  *          The name of the preference function to call, on nsIPrefBranch
   150  * @param   preference
   151  *          The name of the preference
   152  * @param   defaultValue
   153  *          The default value to return in the event the preference has
   154  *          no setting
   155  * @returns The value of the preference, or undefined if there was no
   156  *          user or default value.
   157  */
   158 function getPref(func, preference, defaultValue) {
   159   try {
   160     return gPref[func](preference);
   161   }
   162   catch (e) {
   163   }
   164   return defaultValue;
   165 }
   167 /**
   168  * Constructs a URI to a spec.
   169  * @param   spec
   170  *          The spec to construct a URI to
   171  * @returns The nsIURI constructed.
   172  */
   173 function newURI(spec) {
   174   var ioServ = Cc["@mozilla.org/network/io-service;1"].
   175                getService(Ci.nsIIOService);
   176   return ioServ.newURI(spec, null, null);
   177 }
   179 // Restarts the application checking in with observers first
   180 function restartApp() {
   181   // Notify all windows that an application quit has been requested.
   182   var os = Cc["@mozilla.org/observer-service;1"].
   183            getService(Ci.nsIObserverService);
   184   var cancelQuit = Cc["@mozilla.org/supports-PRBool;1"].
   185                    createInstance(Ci.nsISupportsPRBool);
   186   os.notifyObservers(cancelQuit, "quit-application-requested", null);
   188   // Something aborted the quit process.
   189   if (cancelQuit.data)
   190     return;
   192   var as = Cc["@mozilla.org/toolkit/app-startup;1"].
   193            getService(Ci.nsIAppStartup);
   194   as.quit(Ci.nsIAppStartup.eRestart | Ci.nsIAppStartup.eAttemptQuit);
   195 }
   197 /**
   198  * Checks whether this blocklist element is valid for the current OS and ABI.
   199  * If the element has an "os" attribute then the current OS must appear in
   200  * its comma separated list for the element to be valid. Similarly for the
   201  * xpcomabi attribute.
   202  */
   203 function matchesOSABI(blocklistElement) {
   204   if (blocklistElement.hasAttribute("os")) {
   205     var choices = blocklistElement.getAttribute("os").split(",");
   206     if (choices.length > 0 && choices.indexOf(gApp.OS) < 0)
   207       return false;
   208   }
   210   if (blocklistElement.hasAttribute("xpcomabi")) {
   211     choices = blocklistElement.getAttribute("xpcomabi").split(",");
   212     if (choices.length > 0 && choices.indexOf(gApp.XPCOMABI) < 0)
   213       return false;
   214   }
   216   return true;
   217 }
   219 /**
   220  * Gets the current value of the locale.  It's possible for this preference to
   221  * be localized, so we have to do a little extra work here.  Similar code
   222  * exists in nsHttpHandler.cpp when building the UA string.
   223  */
   224 function getLocale() {
   225   try {
   226       // Get the default branch
   227       var defaultPrefs = gPref.getDefaultBranch(null);
   228       return defaultPrefs.getComplexValue(PREF_GENERAL_USERAGENT_LOCALE,
   229                                           Ci.nsIPrefLocalizedString).data;
   230   } catch (e) {}
   232   return gPref.getCharPref(PREF_GENERAL_USERAGENT_LOCALE);
   233 }
   235 /* Get the distribution pref values, from defaults only */
   236 function getDistributionPrefValue(aPrefName) {
   237   var prefValue = "default";
   239   var defaults = gPref.getDefaultBranch(null);
   240   try {
   241     prefValue = defaults.getCharPref(aPrefName);
   242   } catch (e) {
   243     // use default when pref not found
   244   }
   246   return prefValue;
   247 }
   249 /**
   250  * Parse a string representation of a regular expression. Needed because we
   251  * use the /pattern/flags form (because it's detectable), which is only
   252  * supported as a literal in JS.
   253  *
   254  * @param  aStr
   255  *         String representation of regexp
   256  * @return RegExp instance
   257  */
   258 function parseRegExp(aStr) {
   259   let lastSlash = aStr.lastIndexOf("/");
   260   let pattern = aStr.slice(1, lastSlash);
   261   let flags = aStr.slice(lastSlash + 1);
   262   return new RegExp(pattern, flags);
   263 }
   265 /**
   266  * Manages the Blocklist. The Blocklist is a representation of the contents of
   267  * blocklist.xml and allows us to remotely disable / re-enable blocklisted
   268  * items managed by the Extension Manager with an item's appDisabled property.
   269  * It also blocklists plugins with data from blocklist.xml.
   270  */
   272 function Blocklist() {
   273   let os = getObserverService();
   274   os.addObserver(this, "xpcom-shutdown", false);
   275   gLoggingEnabled = getPref("getBoolPref", PREF_EM_LOGGING_ENABLED, false);
   276   gBlocklistEnabled = getPref("getBoolPref", PREF_BLOCKLIST_ENABLED, true);
   277   gBlocklistLevel = Math.min(getPref("getIntPref", PREF_BLOCKLIST_LEVEL, DEFAULT_LEVEL),
   278                                      MAX_BLOCK_LEVEL);
   279   gPref.addObserver("extensions.blocklist.", this, false);
   280   gPref.addObserver(PREF_EM_LOGGING_ENABLED, this, false);
   281 }
   283 Blocklist.prototype = {
   284   /**
   285    * Extension ID -> array of Version Ranges
   286    * Each value in the version range array is a JS Object that has the
   287    * following properties:
   288    *   "minVersion"  The minimum version in a version range (default = 0)
   289    *   "maxVersion"  The maximum version in a version range (default = *)
   290    *   "targetApps"  Application ID -> array of Version Ranges
   291    *                 (default = current application ID)
   292    *                 Each value in the version range array is a JS Object that
   293    *                 has the following properties:
   294    *                   "minVersion"  The minimum version in a version range
   295    *                                 (default = 0)
   296    *                   "maxVersion"  The maximum version in a version range
   297    *                                 (default = *)
   298    */
   299   _addonEntries: null,
   300   _pluginEntries: null,
   302   observe: function Blocklist_observe(aSubject, aTopic, aData) {
   303     switch (aTopic) {
   304     case "xpcom-shutdown":
   305       let os = getObserverService();
   306       os.removeObserver(this, "xpcom-shutdown");
   307       gPref.removeObserver("extensions.blocklist.", this);
   308       gPref.removeObserver(PREF_EM_LOGGING_ENABLED, this);
   309       break;
   310     case "nsPref:changed":
   311       switch (aData) {
   312         case PREF_EM_LOGGING_ENABLED:
   313           gLoggingEnabled = getPref("getBoolPref", PREF_EM_LOGGING_ENABLED, false);
   314           break;
   315         case PREF_BLOCKLIST_ENABLED:
   316           gBlocklistEnabled = getPref("getBoolPref", PREF_BLOCKLIST_ENABLED, true);
   317           this._loadBlocklist();
   318           this._blocklistUpdated(null, null);
   319           break;
   320         case PREF_BLOCKLIST_LEVEL:
   321           gBlocklistLevel = Math.min(getPref("getIntPref", PREF_BLOCKLIST_LEVEL, DEFAULT_LEVEL),
   322                                      MAX_BLOCK_LEVEL);
   323           this._blocklistUpdated(null, null);
   324           break;
   325       }
   326       break;
   327     }
   328   },
   330   /* See nsIBlocklistService */
   331   isAddonBlocklisted: function Blocklist_isAddonBlocklisted(addon, appVersion, toolkitVersion) {
   332     return this.getAddonBlocklistState(addon, appVersion, toolkitVersion) ==
   333                    Ci.nsIBlocklistService.STATE_BLOCKED;
   334   },
   336   /* See nsIBlocklistService */
   337   getAddonBlocklistState: function Blocklist_getAddonBlocklistState(addon, appVersion, toolkitVersion) {
   338     if (!this._addonEntries)
   339       this._loadBlocklist();
   340     return this._getAddonBlocklistState(addon, this._addonEntries,
   341                                         appVersion, toolkitVersion);
   342   },
   344   /**
   345    * Private version of getAddonBlocklistState that allows the caller to pass in
   346    * the add-on blocklist entries to compare against.
   347    *
   348    * @param   id
   349    *          The ID of the item to get the blocklist state for.
   350    * @param   version
   351    *          The version of the item to get the blocklist state for.
   352    * @param   addonEntries
   353    *          The add-on blocklist entries to compare against.
   354    * @param   appVersion
   355    *          The application version to compare to, will use the current
   356    *          version if null.
   357    * @param   toolkitVersion
   358    *          The toolkit version to compare to, will use the current version if
   359    *          null.
   360    * @returns The blocklist state for the item, one of the STATE constants as
   361    *          defined in nsIBlocklistService.
   362    */
   363   _getAddonBlocklistState: function Blocklist_getAddonBlocklistStateCall(addon,
   364                            addonEntries, appVersion, toolkitVersion) {
   365     if (!gBlocklistEnabled)
   366       return Ci.nsIBlocklistService.STATE_NOT_BLOCKED;
   368     if (!appVersion)
   369       appVersion = gApp.version;
   370     if (!toolkitVersion)
   371       toolkitVersion = gApp.platformVersion;
   373     var blItem = this._findMatchingAddonEntry(addonEntries, addon);
   374     if (!blItem)
   375       return Ci.nsIBlocklistService.STATE_NOT_BLOCKED;
   377     for (let currentblItem of blItem.versions) {
   378       if (currentblItem.includesItem(addon.version, appVersion, toolkitVersion))
   379         return currentblItem.severity >= gBlocklistLevel ? Ci.nsIBlocklistService.STATE_BLOCKED :
   380                                                        Ci.nsIBlocklistService.STATE_SOFTBLOCKED;
   381     }
   382     return Ci.nsIBlocklistService.STATE_NOT_BLOCKED;
   383   },
   385   /**
   386    * Returns the set of prefs of the add-on stored in the blocklist file
   387    * (probably to revert them on disabling).
   388    * @param addon
   389    *        The add-on whose to-be-reset prefs are to be found.
   390    */
   391   _getAddonPrefs: function Blocklist_getAddonPrefs(addon) {
   392     let entry = this._findMatchingAddonEntry(this._addonEntries, addon);
   393     return entry.prefs.slice(0);
   394   },
   396   _findMatchingAddonEntry: function Blocklist_findMatchingAddonEntry(aAddonEntries,
   397                                                                      aAddon) {
   398     if (!aAddon)
   399       return null;
   400     // Returns true if the params object passes the constraints set by entry.
   401     // (For every non-null property in entry, the same key must exist in
   402     // params and value must be the same)
   403     function checkEntry(entry, params) {
   404       for (let [key, value] of entry) {
   405         if (value === null || value === undefined)
   406           continue;
   407         if (params[key]) {
   408           if (value instanceof RegExp) {
   409             if (!value.test(params[key])) {
   410               return false;
   411             }
   412           } else if (value !== params[key]) {
   413             return false;
   414           }
   415         } else {
   416           return false;
   417         }
   418       }
   419       return true;
   420     }
   422     let params = {};
   423     for (let filter of EXTENSION_BLOCK_FILTERS) {
   424       params[filter] = aAddon[filter];
   425     }
   426     if (params.creator)
   427       params.creator = params.creator.name;
   428     for (let entry of aAddonEntries) {
   429       if (checkEntry(entry.attributes, params)) {
   430          return entry;
   431        }
   432      }
   433      return null;
   434   },
   436   /* See nsIBlocklistService */
   437   getAddonBlocklistURL: function Blocklist_getAddonBlocklistURL(addon, appVersion, toolkitVersion) {
   438     if (!gBlocklistEnabled)
   439       return "";
   441     if (!this._addonEntries)
   442       this._loadBlocklist();
   444     let blItem = this._findMatchingAddonEntry(this._addonEntries, addon);
   445     if (!blItem || !blItem.blockID)
   446       return null;
   448     return this._createBlocklistURL(blItem.blockID);
   449   },
   451   _createBlocklistURL: function Blocklist_createBlocklistURL(id) {
   452     let url = Services.urlFormatter.formatURLPref(PREF_BLOCKLIST_ITEM_URL);
   453     url = url.replace(/%blockID%/g, id);
   455     return url;
   456   },
   458   notify: function Blocklist_notify(aTimer) {
   459     if (!gBlocklistEnabled)
   460       return;
   462     try {
   463       var dsURI = gPref.getCharPref(PREF_BLOCKLIST_URL);
   464     }
   465     catch (e) {
   466       LOG("Blocklist::notify: The " + PREF_BLOCKLIST_URL + " preference" +
   467           " is missing!");
   468       return;
   469     }
   471     var pingCountVersion = getPref("getIntPref", PREF_BLOCKLIST_PINGCOUNTVERSION, 0);
   472     var pingCountTotal = getPref("getIntPref", PREF_BLOCKLIST_PINGCOUNTTOTAL, 1);
   473     var daysSinceLastPing = 0;
   474     if (pingCountVersion == 0) {
   475       daysSinceLastPing = "new";
   476     }
   477     else {
   478       // Seconds in one day is used because nsIUpdateTimerManager stores the
   479       // last update time in seconds.
   480       let secondsInDay = 60 * 60 * 24;
   481       let lastUpdateTime = getPref("getIntPref", PREF_BLOCKLIST_LASTUPDATETIME, 0);
   482       if (lastUpdateTime == 0) {
   483         daysSinceLastPing = "invalid";
   484       }
   485       else {
   486         let now = Math.round(Date.now() / 1000);
   487         daysSinceLastPing = Math.floor((now - lastUpdateTime) / secondsInDay);
   488       }
   490       if (daysSinceLastPing == 0 || daysSinceLastPing == "invalid") {
   491         pingCountVersion = pingCountTotal = "invalid";
   492       }
   493     }
   495     if (pingCountVersion < 1)
   496       pingCountVersion = 1;
   497     if (pingCountTotal < 1)
   498       pingCountTotal = 1;
   500     dsURI = dsURI.replace(/%APP_ID%/g, gApp.ID);
   501     dsURI = dsURI.replace(/%APP_VERSION%/g, gApp.version);
   502     dsURI = dsURI.replace(/%PRODUCT%/g, gApp.name);
   503     dsURI = dsURI.replace(/%VERSION%/g, gApp.version);
   504     dsURI = dsURI.replace(/%BUILD_ID%/g, gApp.appBuildID);
   505     dsURI = dsURI.replace(/%BUILD_TARGET%/g, gApp.OS + "_" + gABI);
   506     dsURI = dsURI.replace(/%OS_VERSION%/g, gOSVersion);
   507     dsURI = dsURI.replace(/%LOCALE%/g, getLocale());
   508     dsURI = dsURI.replace(/%CHANNEL%/g, UpdateChannel.get());
   509     dsURI = dsURI.replace(/%PLATFORM_VERSION%/g, gApp.platformVersion);
   510     dsURI = dsURI.replace(/%DISTRIBUTION%/g,
   511                       getDistributionPrefValue(PREF_APP_DISTRIBUTION));
   512     dsURI = dsURI.replace(/%DISTRIBUTION_VERSION%/g,
   513                       getDistributionPrefValue(PREF_APP_DISTRIBUTION_VERSION));
   514     dsURI = dsURI.replace(/%PING_COUNT%/g, pingCountVersion);
   515     dsURI = dsURI.replace(/%TOTAL_PING_COUNT%/g, pingCountTotal);
   516     dsURI = dsURI.replace(/%DAYS_SINCE_LAST_PING%/g, daysSinceLastPing);
   517     dsURI = dsURI.replace(/\+/g, "%2B");
   519     // Under normal operations it will take around 5,883,516 years before the
   520     // preferences used to store pingCountVersion and pingCountTotal will rollover
   521     // so this code doesn't bother trying to do the "right thing" here.
   522     if (pingCountVersion != "invalid") {
   523       pingCountVersion++;
   524       if (pingCountVersion > 2147483647) {
   525         // Rollover to -1 if the value is greater than what is support by an
   526         // integer preference. The -1 indicates that the counter has been reset.
   527         pingCountVersion = -1;
   528       }
   529       gPref.setIntPref(PREF_BLOCKLIST_PINGCOUNTVERSION, pingCountVersion);
   530     }
   532     if (pingCountTotal != "invalid") {
   533       pingCountTotal++;
   534       if (pingCountTotal > 2147483647) {
   535         // Rollover to 1 if the value is greater than what is support by an
   536         // integer preference.
   537         pingCountTotal = -1;
   538       }
   539       gPref.setIntPref(PREF_BLOCKLIST_PINGCOUNTTOTAL, pingCountTotal);
   540     }
   542     // Verify that the URI is valid
   543     try {
   544       var uri = newURI(dsURI);
   545     }
   546     catch (e) {
   547       LOG("Blocklist::notify: There was an error creating the blocklist URI\r\n" +
   548           "for: " + dsURI + ", error: " + e);
   549       return;
   550     }
   552     LOG("Blocklist::notify: Requesting " + uri.spec);
   553     var request = Cc["@mozilla.org/xmlextras/xmlhttprequest;1"].
   554                   createInstance(Ci.nsIXMLHttpRequest);
   555     request.open("GET", uri.spec, true);
   556     request.channel.notificationCallbacks = new gCertUtils.BadCertHandler();
   557     request.overrideMimeType("text/xml");
   558     request.setRequestHeader("Cache-Control", "no-cache");
   559     request.QueryInterface(Components.interfaces.nsIJSXMLHttpRequest);
   561     var self = this;
   562     request.addEventListener("error", function errorEventListener(event) {
   563                                       self.onXMLError(event); }, false);
   564     request.addEventListener("load", function loadEventListener(event) {
   565                                      self.onXMLLoad(event);  }, false);
   566     request.send(null);
   568     // When the blocklist loads we need to compare it to the current copy so
   569     // make sure we have loaded it.
   570     if (!this._addonEntries)
   571       this._loadBlocklist();
   572   },
   574   onXMLLoad: Task.async(function* (aEvent) {
   575     let request = aEvent.target;
   576     try {
   577       gCertUtils.checkCert(request.channel);
   578     }
   579     catch (e) {
   580       LOG("Blocklist::onXMLLoad: " + e);
   581       return;
   582     }
   583     let responseXML = request.responseXML;
   584     if (!responseXML || responseXML.documentElement.namespaceURI == XMLURI_PARSE_ERROR ||
   585         (request.status != 200 && request.status != 0)) {
   586       LOG("Blocklist::onXMLLoad: there was an error during load");
   587       return;
   588     }
   590     var oldAddonEntries = this._addonEntries;
   591     var oldPluginEntries = this._pluginEntries;
   592     this._addonEntries = [];
   593     this._pluginEntries = [];
   595     this._loadBlocklistFromString(request.responseText);
   596     this._blocklistUpdated(oldAddonEntries, oldPluginEntries);
   598     try {
   599       let path = OS.Path.join(OS.Constants.Path.profileDir, FILE_BLOCKLIST);
   600       yield OS.File.writeAtomic(path, request.responseText, {tmpPath: path + ".tmp"});
   601     } catch (e) {
   602       LOG("Blocklist::onXMLLoad: " + e);
   603     }
   604   }),
   606   onXMLError: function Blocklist_onXMLError(aEvent) {
   607     try {
   608       var request = aEvent.target;
   609       // the following may throw (e.g. a local file or timeout)
   610       var status = request.status;
   611     }
   612     catch (e) {
   613       request = aEvent.target.channel.QueryInterface(Ci.nsIRequest);
   614       status = request.status;
   615     }
   616     var statusText = "nsIXMLHttpRequest channel unavailable";
   617     // When status is 0 we don't have a valid channel.
   618     if (status != 0) {
   619       try {
   620         statusText = request.statusText;
   621       } catch (e) {
   622       }
   623     }
   624     LOG("Blocklist:onError: There was an error loading the blocklist file\r\n" +
   625         statusText);
   626   },
   628   /**
   629    * Finds the newest blocklist file from the application and the profile and
   630    * load it or does nothing if neither exist.
   631    */
   632   _loadBlocklist: function Blocklist_loadBlocklist() {
   633     this._addonEntries = [];
   634     this._pluginEntries = [];
   635     var profFile = FileUtils.getFile(KEY_PROFILEDIR, [FILE_BLOCKLIST]);
   636     if (profFile.exists()) {
   637       this._loadBlocklistFromFile(profFile);
   638       return;
   639     }
   640     var appFile = FileUtils.getFile(KEY_APPDIR, [FILE_BLOCKLIST]);
   641     if (appFile.exists()) {
   642       this._loadBlocklistFromFile(appFile);
   643       return;
   644     }
   645     LOG("Blocklist::_loadBlocklist: no XML File found");
   646   },
   648   /**
   649 #    The blocklist XML file looks something like this:
   650 #
   651 #    <blocklist xmlns="http://www.mozilla.org/2006/addons-blocklist">
   652 #      <emItems>
   653 #        <emItem id="item_1@domain" blockID="i1">
   654 #          <prefs>
   655 #            <pref>accessibility.accesskeycausesactivation</pref>
   656 #            <pref>accessibility.blockautorefresh</pref>
   657 #          </prefs>
   658 #          <versionRange minVersion="1.0" maxVersion="2.0.*">
   659 #            <targetApplication id="{ec8030f7-c20a-464f-9b0e-13a3a9e97384}">
   660 #              <versionRange minVersion="1.5" maxVersion="1.5.*"/>
   661 #              <versionRange minVersion="1.7" maxVersion="1.7.*"/>
   662 #            </targetApplication>
   663 #            <targetApplication id="toolkit@mozilla.org">
   664 #              <versionRange minVersion="1.9" maxVersion="1.9.*"/>
   665 #            </targetApplication>
   666 #          </versionRange>
   667 #          <versionRange minVersion="3.0" maxVersion="3.0.*">
   668 #            <targetApplication id="{ec8030f7-c20a-464f-9b0e-13a3a9e97384}">
   669 #              <versionRange minVersion="1.5" maxVersion="1.5.*"/>
   670 #            </targetApplication>
   671 #            <targetApplication id="toolkit@mozilla.org">
   672 #              <versionRange minVersion="1.9" maxVersion="1.9.*"/>
   673 #            </targetApplication>
   674 #          </versionRange>
   675 #        </emItem>
   676 #        <emItem id="item_2@domain" blockID="i2">
   677 #          <versionRange minVersion="3.1" maxVersion="4.*"/>
   678 #        </emItem>
   679 #        <emItem id="item_3@domain">
   680 #          <versionRange>
   681 #            <targetApplication id="{ec8030f7-c20a-464f-9b0e-13a3a9e97384}">
   682 #              <versionRange minVersion="1.5" maxVersion="1.5.*"/>
   683 #            </targetApplication>
   684 #          </versionRange>
   685 #        </emItem>
   686 #        <emItem id="item_4@domain" blockID="i3">
   687 #          <versionRange>
   688 #            <targetApplication>
   689 #              <versionRange minVersion="1.5" maxVersion="1.5.*"/>
   690 #            </targetApplication>
   691 #          </versionRange>
   692 #        <emItem id="/@badperson\.com$/"/>
   693 #      </emItems>
   694 #      <pluginItems>
   695 #        <pluginItem blockID="i4">
   696 #          <!-- All match tags must match a plugin to blocklist a plugin -->
   697 #          <match name="name" exp="some plugin"/>
   698 #          <match name="description" exp="1[.]2[.]3"/>
   699 #        </pluginItem>
   700 #      </pluginItems>
   701 #    </blocklist>
   702    */
   704   _loadBlocklistFromFile: function Blocklist_loadBlocklistFromFile(file) {
   705     if (!gBlocklistEnabled) {
   706       LOG("Blocklist::_loadBlocklistFromFile: blocklist is disabled");
   707       return;
   708     }
   710     if (!file.exists()) {
   711       LOG("Blocklist::_loadBlocklistFromFile: XML File does not exist " + file.path);
   712       return;
   713     }
   715     let text = "";
   716     let fstream = null;
   717     let cstream = null;
   719     try {
   720       fstream = Components.classes["@mozilla.org/network/file-input-stream;1"]
   721                           .createInstance(Components.interfaces.nsIFileInputStream);
   722       cstream = Components.classes["@mozilla.org/intl/converter-input-stream;1"]
   723                           .createInstance(Components.interfaces.nsIConverterInputStream);
   725       fstream.init(file, FileUtils.MODE_RDONLY, FileUtils.PERMS_FILE, 0);
   726       cstream.init(fstream, "UTF-8", 0, 0);
   728       let (str = {}) {
   729         let read = 0;
   731         do {
   732           read = cstream.readString(0xffffffff, str); // read as much as we can and put it in str.value
   733           text += str.value;
   734         } while (read != 0);
   735       }
   736     } catch (e) {
   737       LOG("Blocklist::_loadBlocklistFromFile: Failed to load XML file " + e);
   738     } finally {
   739       cstream.close();
   740       fstream.close();
   741     }
   743     text && this._loadBlocklistFromString(text);
   744   },
   746   _loadBlocklistFromString : function Blocklist_loadBlocklistFromString(text) {
   747     try {
   748       var parser = Cc["@mozilla.org/xmlextras/domparser;1"].
   749                    createInstance(Ci.nsIDOMParser);
   750       var doc = parser.parseFromString(text, "text/xml");
   751       if (doc.documentElement.namespaceURI != XMLURI_BLOCKLIST) {
   752         LOG("Blocklist::_loadBlocklistFromFile: aborting due to incorrect " +
   753             "XML Namespace.\r\nExpected: " + XMLURI_BLOCKLIST + "\r\n" +
   754             "Received: " + doc.documentElement.namespaceURI);
   755         return;
   756       }
   758       var childNodes = doc.documentElement.childNodes;
   759       for (let element of childNodes) {
   760         if (!(element instanceof Ci.nsIDOMElement))
   761           continue;
   762         switch (element.localName) {
   763         case "emItems":
   764           this._addonEntries = this._processItemNodes(element.childNodes, "em",
   765                                                       this._handleEmItemNode);
   766           break;
   767         case "pluginItems":
   768           this._pluginEntries = this._processItemNodes(element.childNodes, "plugin",
   769                                                        this._handlePluginItemNode);
   770           break;
   771         default:
   772           Services.obs.notifyObservers(element,
   773                                        "blocklist-data-" + element.localName,
   774                                        null);
   775         }
   776       }
   777     }
   778     catch (e) {
   779       LOG("Blocklist::_loadBlocklistFromFile: Error constructing blocklist " + e);
   780       return;
   781     }
   782   },
   784   _processItemNodes: function Blocklist_processItemNodes(itemNodes, prefix, handler) {
   785     var result = [];
   786     var itemName = prefix + "Item";
   787     for (var i = 0; i < itemNodes.length; ++i) {
   788       var blocklistElement = itemNodes.item(i);
   789       if (!(blocklistElement instanceof Ci.nsIDOMElement) ||
   790           blocklistElement.localName != itemName)
   791         continue;
   793       handler(blocklistElement, result);
   794     }
   795     return result;
   796   },
   798   _handleEmItemNode: function Blocklist_handleEmItemNode(blocklistElement, result) {
   799     if (!matchesOSABI(blocklistElement))
   800       return;
   802     let blockEntry = {
   803       versions: [],
   804       prefs: [],
   805       blockID: null,
   806       attributes: new Map()
   807       // Atleast one of EXTENSION_BLOCK_FILTERS must get added to attributes
   808     };
   810     // Any filter starting with '/' is interpreted as a regex. So if an attribute
   811     // starts with a '/' it must be checked via a regex.
   812     function regExpCheck(attr) {
   813       return attr.startsWith("/") ? parseRegExp(attr) : attr;
   814     }
   816     for (let filter of EXTENSION_BLOCK_FILTERS) {
   817       let attr = blocklistElement.getAttribute(filter);
   818       if (attr)
   819         blockEntry.attributes.set(filter, regExpCheck(attr));
   820     }
   822     var childNodes = blocklistElement.childNodes;
   824     for (let x = 0; x < childNodes.length; x++) {
   825       var childElement = childNodes.item(x);
   826       if (!(childElement instanceof Ci.nsIDOMElement))
   827         continue;
   828       if (childElement.localName === "prefs") {
   829         let prefElements = childElement.childNodes;
   830         for (let i = 0; i < prefElements.length; i++) {
   831           let prefElement = prefElements.item(i);
   832           if (!(prefElement instanceof Ci.nsIDOMElement) ||
   833               prefElement.localName !== "pref")
   834             continue;
   835           blockEntry.prefs.push(prefElement.textContent);
   836         }
   837       }
   838       else if (childElement.localName === "versionRange")
   839         blockEntry.versions.push(new BlocklistItemData(childElement));
   840     }
   841     // if only the extension ID is specified block all versions of the
   842     // extension for the current application.
   843     if (blockEntry.versions.length == 0)
   844       blockEntry.versions.push(new BlocklistItemData(null));
   846     blockEntry.blockID = blocklistElement.getAttribute("blockID");
   848     result.push(blockEntry);
   849   },
   851   _handlePluginItemNode: function Blocklist_handlePluginItemNode(blocklistElement, result) {
   852     if (!matchesOSABI(blocklistElement))
   853       return;
   855     var matchNodes = blocklistElement.childNodes;
   856     var blockEntry = {
   857       matches: {},
   858       versions: [],
   859       blockID: null,
   860     };
   861     var hasMatch = false;
   862     for (var x = 0; x < matchNodes.length; ++x) {
   863       var matchElement = matchNodes.item(x);
   864       if (!(matchElement instanceof Ci.nsIDOMElement))
   865         continue;
   866       if (matchElement.localName == "match") {
   867         var name = matchElement.getAttribute("name");
   868         var exp = matchElement.getAttribute("exp");
   869         try {
   870           blockEntry.matches[name] = new RegExp(exp, "m");
   871           hasMatch = true;
   872         } catch (e) {
   873           // Ignore invalid regular expressions
   874         }
   875       }
   876       if (matchElement.localName == "versionRange")
   877         blockEntry.versions.push(new BlocklistItemData(matchElement));
   878     }
   879     // Plugin entries require *something* to match to an actual plugin
   880     if (!hasMatch)
   881       return;
   882     // Add a default versionRange if there wasn't one specified
   883     if (blockEntry.versions.length == 0)
   884       blockEntry.versions.push(new BlocklistItemData(null));
   886     blockEntry.blockID = blocklistElement.getAttribute("blockID");
   888     result.push(blockEntry);
   889   },
   891   /* See nsIBlocklistService */
   892   getPluginBlocklistState: function Blocklist_getPluginBlocklistState(plugin,
   893                            appVersion, toolkitVersion) {
   894     if (!this._pluginEntries)
   895       this._loadBlocklist();
   896     return this._getPluginBlocklistState(plugin, this._pluginEntries,
   897                                          appVersion, toolkitVersion);
   898   },
   900   /**
   901    * Private version of getPluginBlocklistState that allows the caller to pass in
   902    * the plugin blocklist entries.
   903    *
   904    * @param   plugin
   905    *          The nsIPluginTag to get the blocklist state for.
   906    * @param   pluginEntries
   907    *          The plugin blocklist entries to compare against.
   908    * @param   appVersion
   909    *          The application version to compare to, will use the current
   910    *          version if null.
   911    * @param   toolkitVersion
   912    *          The toolkit version to compare to, will use the current version if
   913    *          null.
   914    * @returns The blocklist state for the item, one of the STATE constants as
   915    *          defined in nsIBlocklistService.
   916    */
   917   _getPluginBlocklistState: function Blocklist_getPluginBlocklistState(plugin,
   918                             pluginEntries, appVersion, toolkitVersion) {
   919     if (!gBlocklistEnabled)
   920       return Ci.nsIBlocklistService.STATE_NOT_BLOCKED;
   922     if (!appVersion)
   923       appVersion = gApp.version;
   924     if (!toolkitVersion)
   925       toolkitVersion = gApp.platformVersion;
   927     for each (var blockEntry in pluginEntries) {
   928       var matchFailed = false;
   929       for (var name in blockEntry.matches) {
   930         if (!(name in plugin) ||
   931             typeof(plugin[name]) != "string" ||
   932             !blockEntry.matches[name].test(plugin[name])) {
   933           matchFailed = true;
   934           break;
   935         }
   936       }
   938       if (matchFailed)
   939         continue;
   941       for (let blockEntryVersion of blockEntry.versions) {
   942         if (blockEntryVersion.includesItem(plugin.version, appVersion,
   943                                                 toolkitVersion)) {
   944           if (blockEntryVersion.severity >= gBlocklistLevel)
   945             return Ci.nsIBlocklistService.STATE_BLOCKED;
   946           if (blockEntryVersion.severity == SEVERITY_OUTDATED) {
   947             let vulnerabilityStatus = blockEntryVersion.vulnerabilityStatus;
   948             if (vulnerabilityStatus == VULNERABILITYSTATUS_UPDATE_AVAILABLE)
   949               return Ci.nsIBlocklistService.STATE_VULNERABLE_UPDATE_AVAILABLE;
   950             if (vulnerabilityStatus == VULNERABILITYSTATUS_NO_UPDATE)
   951               return Ci.nsIBlocklistService.STATE_VULNERABLE_NO_UPDATE;
   952             return Ci.nsIBlocklistService.STATE_OUTDATED;
   953           }
   954           return Ci.nsIBlocklistService.STATE_SOFTBLOCKED;
   955         }
   956       }
   957     }
   959     return Ci.nsIBlocklistService.STATE_NOT_BLOCKED;
   960   },
   962   /* See nsIBlocklistService */
   963   getPluginBlocklistURL: function Blocklist_getPluginBlocklistURL(plugin) {
   964     if (!gBlocklistEnabled)
   965       return "";
   967     if (!this._pluginEntries)
   968       this._loadBlocklist();
   970     for each (let blockEntry in this._pluginEntries) {
   971       let matchFailed = false;
   972       for (let name in blockEntry.matches) {
   973         if (!(name in plugin) ||
   974             typeof(plugin[name]) != "string" ||
   975             !blockEntry.matches[name].test(plugin[name])) {
   976           matchFailed = true;
   977           break;
   978         }
   979       }
   981       if (!matchFailed) {
   982         if(!blockEntry.blockID)
   983           return null;
   984         else
   985           return this._createBlocklistURL(blockEntry.blockID);
   986       }
   987     }
   988   },
   990   _blocklistUpdated: function Blocklist_blocklistUpdated(oldAddonEntries, oldPluginEntries) {
   991     var addonList = [];
   993     // A helper function that reverts the prefs passed to default values.
   994     function resetPrefs(prefs) {
   995       for (let pref of prefs)
   996         gPref.clearUserPref(pref);
   997     }
   998     var self = this;
   999     const types = ["extension", "theme", "locale", "dictionary", "service"];
  1000     AddonManager.getAddonsByTypes(types, function blocklistUpdated_getAddonsByTypes(addons) {
  1002       for (let addon of addons) {
  1003         let oldState = Ci.nsIBlocklistService.STATE_NOTBLOCKED;
  1004         if (oldAddonEntries)
  1005           oldState = self._getAddonBlocklistState(addon, oldAddonEntries);
  1006         let state = self.getAddonBlocklistState(addon);
  1008         LOG("Blocklist state for " + addon.id + " changed from " +
  1009             oldState + " to " + state);
  1011         // We don't want to re-warn about add-ons
  1012         if (state == oldState)
  1013           continue;
  1015         if (state === Ci.nsIBlocklistService.STATE_BLOCKED) {
  1016           // It's a hard block. We must reset certain preferences.
  1017           let prefs = self._getAddonPrefs(addon);
  1018           resetPrefs(prefs);
  1021         // Ensure that softDisabled is false if the add-on is not soft blocked
  1022         if (state != Ci.nsIBlocklistService.STATE_SOFTBLOCKED)
  1023           addon.softDisabled = false;
  1025         // Don't warn about add-ons becoming unblocked.
  1026         if (state == Ci.nsIBlocklistService.STATE_NOT_BLOCKED)
  1027           continue;
  1029         // If an add-on has dropped from hard to soft blocked just mark it as
  1030         // soft disabled and don't warn about it.
  1031         if (state == Ci.nsIBlocklistService.STATE_SOFTBLOCKED &&
  1032             oldState == Ci.nsIBlocklistService.STATE_BLOCKED) {
  1033           addon.softDisabled = true;
  1034           continue;
  1037         // If the add-on is already disabled for some reason then don't warn
  1038         // about it
  1039         if (!addon.isActive)
  1040           continue;
  1042         addonList.push({
  1043           name: addon.name,
  1044           version: addon.version,
  1045           icon: addon.iconURL,
  1046           disable: false,
  1047           blocked: state == Ci.nsIBlocklistService.STATE_BLOCKED,
  1048           item: addon,
  1049           url: self.getAddonBlocklistURL(addon),
  1050         });
  1053       AddonManagerPrivate.updateAddonAppDisabledStates();
  1055       var phs = Cc["@mozilla.org/plugin/host;1"].
  1056                 getService(Ci.nsIPluginHost);
  1057       var plugins = phs.getPluginTags();
  1059       for (let plugin of plugins) {
  1060         let oldState = -1;
  1061         if (oldPluginEntries)
  1062           oldState = self._getPluginBlocklistState(plugin, oldPluginEntries);
  1063         let state = self.getPluginBlocklistState(plugin);
  1064         LOG("Blocklist state for " + plugin.name + " changed from " +
  1065             oldState + " to " + state);
  1066         // We don't want to re-warn about items
  1067         if (state == oldState)
  1068           continue;
  1070         if (oldState == Ci.nsIBlocklistService.STATE_BLOCKED) {
  1071           if (state == Ci.nsIBlocklistService.STATE_SOFTBLOCKED)
  1072             plugin.enabledState = Ci.nsIPluginTag.STATE_DISABLED;
  1074         else if (!plugin.disabled && state != Ci.nsIBlocklistService.STATE_NOT_BLOCKED) {
  1075           if (state == Ci.nsIBlocklistService.STATE_OUTDATED) {
  1076             gPref.setBoolPref(PREF_PLUGINS_NOTIFYUSER, true);
  1078           else if (state != Ci.nsIBlocklistService.STATE_VULNERABLE_UPDATE_AVAILABLE &&
  1079                    state != Ci.nsIBlocklistService.STATE_VULNERABLE_NO_UPDATE) {
  1080             addonList.push({
  1081               name: plugin.name,
  1082               version: plugin.version,
  1083               icon: "chrome://mozapps/skin/plugins/pluginGeneric.png",
  1084               disable: false,
  1085               blocked: state == Ci.nsIBlocklistService.STATE_BLOCKED,
  1086               item: plugin,
  1087               url: self.getPluginBlocklistURL(plugin),
  1088             });
  1093       if (addonList.length == 0) {
  1094         Services.obs.notifyObservers(self, "blocklist-updated", "");
  1095         return;
  1098       if ("@mozilla.org/addons/blocklist-prompt;1" in Cc) {
  1099         try {
  1100           let blockedPrompter = Cc["@mozilla.org/addons/blocklist-prompt;1"]
  1101                                  .getService(Ci.nsIBlocklistPrompt);
  1102           blockedPrompter.prompt(addonList);
  1103         } catch (e) {
  1104           LOG(e);
  1106         Services.obs.notifyObservers(self, "blocklist-updated", "");
  1107         return;
  1110       var args = {
  1111         restart: false,
  1112         list: addonList
  1113       };
  1114       // This lets the dialog get the raw js object
  1115       args.wrappedJSObject = args;
  1117       /*
  1118         Some tests run without UI, so the async code listens to a message
  1119         that can be sent programatically
  1120       */
  1121       let applyBlocklistChanges = function blocklistUpdated_applyBlocklistChanges() {
  1122         for (let addon of addonList) {
  1123           if (!addon.disable)
  1124             continue;
  1126           if (addon.item instanceof Ci.nsIPluginTag)
  1127             addon.item.enabledState = Ci.nsIPluginTag.STATE_DISABLED;
  1128           else {
  1129             // This add-on is softblocked.
  1130             addon.item.softDisabled = true;
  1131             // We must revert certain prefs.
  1132             let prefs = self._getAddonPrefs(addon.item);
  1133             resetPrefs(prefs);
  1137         if (args.restart)
  1138           restartApp();
  1140         Services.obs.notifyObservers(self, "blocklist-updated", "");
  1141         Services.obs.removeObserver(applyBlocklistChanges, "addon-blocklist-closed");
  1144       Services.obs.addObserver(applyBlocklistChanges, "addon-blocklist-closed", false);
  1146       if (getPref("getBoolPref", PREF_BLOCKLIST_SUPPRESSUI, false)) {
  1147         applyBlocklistChanges();
  1148         return;
  1151       function blocklistUnloadHandler(event) {
  1152         if (event.target.location == URI_BLOCKLIST_DIALOG) {
  1153           applyBlocklistChanges();
  1154           blocklistWindow.removeEventListener("unload", blocklistUnloadHandler);
  1158       let blocklistWindow = Services.ww.openWindow(null, URI_BLOCKLIST_DIALOG, "",
  1159                               "chrome,centerscreen,dialog,titlebar", args);
  1160       if (blocklistWindow)
  1161         blocklistWindow.addEventListener("unload", blocklistUnloadHandler, false);
  1162     });
  1163   },
  1165   classID: Components.ID("{66354bc9-7ed1-4692-ae1d-8da97d6b205e}"),
  1166   QueryInterface: XPCOMUtils.generateQI([Ci.nsIObserver,
  1167                                          Ci.nsIBlocklistService,
  1168                                          Ci.nsITimerCallback]),
  1169 };
  1171 /**
  1172  * Helper for constructing a blocklist.
  1173  */
  1174 function BlocklistItemData(versionRangeElement) {
  1175   var versionRange = this.getBlocklistVersionRange(versionRangeElement);
  1176   this.minVersion = versionRange.minVersion;
  1177   this.maxVersion = versionRange.maxVersion;
  1178   if (versionRangeElement && versionRangeElement.hasAttribute("severity"))
  1179     this.severity = versionRangeElement.getAttribute("severity");
  1180   else
  1181     this.severity = DEFAULT_SEVERITY;
  1182   if (versionRangeElement && versionRangeElement.hasAttribute("vulnerabilitystatus")) {
  1183     this.vulnerabilityStatus = versionRangeElement.getAttribute("vulnerabilitystatus");
  1184   } else {
  1185     this.vulnerabilityStatus = VULNERABILITYSTATUS_NONE;
  1187   this.targetApps = { };
  1188   var found = false;
  1190   if (versionRangeElement) {
  1191     for (var i = 0; i < versionRangeElement.childNodes.length; ++i) {
  1192       var targetAppElement = versionRangeElement.childNodes.item(i);
  1193       if (!(targetAppElement instanceof Ci.nsIDOMElement) ||
  1194           targetAppElement.localName != "targetApplication")
  1195         continue;
  1196       found = true;
  1197       // default to the current application if id is not provided.
  1198       var appID = targetAppElement.hasAttribute("id") ? targetAppElement.getAttribute("id") : gApp.ID;
  1199       this.targetApps[appID] = this.getBlocklistAppVersions(targetAppElement);
  1202   // Default to all versions of the current application when no targetApplication
  1203   // elements were found
  1204   if (!found)
  1205     this.targetApps[gApp.ID] = this.getBlocklistAppVersions(null);
  1208 BlocklistItemData.prototype = {
  1209   /**
  1210    * Tests if a version of an item is included in the version range and target
  1211    * application information represented by this BlocklistItemData using the
  1212    * provided application and toolkit versions.
  1213    * @param   version
  1214    *          The version of the item being tested.
  1215    * @param   appVersion
  1216    *          The application version to test with.
  1217    * @param   toolkitVersion
  1218    *          The toolkit version to test with.
  1219    * @returns True if the version range covers the item version and application
  1220    *          or toolkit version.
  1221    */
  1222   includesItem: function BlocklistItemData_includesItem(version, appVersion, toolkitVersion) {
  1223     // Some platforms have no version for plugins, these don't match if there
  1224     // was a min/maxVersion provided
  1225     if (!version && (this.minVersion || this.maxVersion))
  1226       return false;
  1228     // Check if the item version matches
  1229     if (!this.matchesRange(version, this.minVersion, this.maxVersion))
  1230       return false;
  1232     // Check if the application version matches
  1233     if (this.matchesTargetRange(gApp.ID, appVersion))
  1234       return true;
  1236     // Check if the toolkit version matches
  1237     return this.matchesTargetRange(TOOLKIT_ID, toolkitVersion);
  1238   },
  1240   /**
  1241    * Checks if a version is higher than or equal to the minVersion (if provided)
  1242    * and lower than or equal to the maxVersion (if provided).
  1243    * @param   version
  1244    *          The version to test.
  1245    * @param   minVersion
  1246    *          The minimum version. If null it is assumed that version is always
  1247    *          larger.
  1248    * @param   maxVersion
  1249    *          The maximum version. If null it is assumed that version is always
  1250    *          smaller.
  1251    */
  1252   matchesRange: function BlocklistItemData_matchesRange(version, minVersion, maxVersion) {
  1253     if (minVersion && gVersionChecker.compare(version, minVersion) < 0)
  1254       return false;
  1255     if (maxVersion && gVersionChecker.compare(version, maxVersion) > 0)
  1256       return false;
  1257     return true;
  1258   },
  1260   /**
  1261    * Tests if there is a matching range for the given target application id and
  1262    * version.
  1263    * @param   appID
  1264    *          The application ID to test for, may be for an application or toolkit
  1265    * @param   appVersion
  1266    *          The version of the application to test for.
  1267    * @returns True if this version range covers the application version given.
  1268    */
  1269   matchesTargetRange: function BlocklistItemData_matchesTargetRange(appID, appVersion) {
  1270     var blTargetApp = this.targetApps[appID];
  1271     if (!blTargetApp)
  1272       return false;
  1274     for (let app of blTargetApp) {
  1275       if (this.matchesRange(appVersion, app.minVersion, app.maxVersion))
  1276         return true;
  1279     return false;
  1280   },
  1282   /**
  1283    * Retrieves a version range (e.g. minVersion and maxVersion) for a
  1284    * blocklist item's targetApplication element.
  1285    * @param   targetAppElement
  1286    *          A targetApplication blocklist element.
  1287    * @returns An array of JS objects with the following properties:
  1288    *          "minVersion"  The minimum version in a version range (default = null).
  1289    *          "maxVersion"  The maximum version in a version range (default = null).
  1290    */
  1291   getBlocklistAppVersions: function BlocklistItemData_getBlocklistAppVersions(targetAppElement) {
  1292     var appVersions = [ ];
  1294     if (targetAppElement) {
  1295       for (var i = 0; i < targetAppElement.childNodes.length; ++i) {
  1296         var versionRangeElement = targetAppElement.childNodes.item(i);
  1297         if (!(versionRangeElement instanceof Ci.nsIDOMElement) ||
  1298             versionRangeElement.localName != "versionRange")
  1299           continue;
  1300         appVersions.push(this.getBlocklistVersionRange(versionRangeElement));
  1303     // return minVersion = null and maxVersion = null if no specific versionRange
  1304     // elements were found
  1305     if (appVersions.length == 0)
  1306       appVersions.push(this.getBlocklistVersionRange(null));
  1307     return appVersions;
  1308   },
  1310   /**
  1311    * Retrieves a version range (e.g. minVersion and maxVersion) for a blocklist
  1312    * versionRange element.
  1313    * @param   versionRangeElement
  1314    *          The versionRange blocklist element.
  1315    * @returns A JS object with the following properties:
  1316    *          "minVersion"  The minimum version in a version range (default = null).
  1317    *          "maxVersion"  The maximum version in a version range (default = null).
  1318    */
  1319   getBlocklistVersionRange: function BlocklistItemData_getBlocklistVersionRange(versionRangeElement) {
  1320     var minVersion = null;
  1321     var maxVersion = null;
  1322     if (!versionRangeElement)
  1323       return { minVersion: minVersion, maxVersion: maxVersion };
  1325     if (versionRangeElement.hasAttribute("minVersion"))
  1326       minVersion = versionRangeElement.getAttribute("minVersion");
  1327     if (versionRangeElement.hasAttribute("maxVersion"))
  1328       maxVersion = versionRangeElement.getAttribute("maxVersion");
  1330     return { minVersion: minVersion, maxVersion: maxVersion };
  1332 };
  1334 this.NSGetFactory = XPCOMUtils.generateNSGetFactory([Blocklist]);

mercurial