home *** CD-ROM | disk | FTP | other *** search
/ Freelog 112 / FreelogNo112-NovembreDecembre2012.iso / Programmation / RJ_TextEd / RJ_TextEd_Portable.exe / components / nsBlocklistService.js < prev    next >
Text File  |  2010-01-01  |  40KB  |  1,131 lines

  1. /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
  2. /*
  3. //@line 41 "e:\builds\moz2_slave\rel-m-rel-xr-w32-bld\build\toolkit\mozapps\extensions\nsBlocklistService.js"
  4. */
  5.  
  6. "use strict";
  7.  
  8. const Cc = Components.classes;
  9. const Ci = Components.interfaces;
  10. const Cr = Components.results;
  11.  
  12. Components.utils.import("resource://gre/modules/XPCOMUtils.jsm");
  13. Components.utils.import("resource://gre/modules/FileUtils.jsm");
  14. Components.utils.import("resource://gre/modules/AddonManager.jsm");
  15. Components.utils.import("resource://gre/modules/Services.jsm");
  16.  
  17. const TOOLKIT_ID                      = "toolkit@mozilla.org"
  18. const KEY_PROFILEDIR                  = "ProfD";
  19. const KEY_APPDIR                      = "XCurProcD";
  20. const FILE_BLOCKLIST                  = "blocklist.xml";
  21. const PREF_BLOCKLIST_LASTUPDATETIME   = "app.update.lastUpdateTime.blocklist-background-update-timer";
  22. const PREF_BLOCKLIST_URL              = "extensions.blocklist.url";
  23. const PREF_BLOCKLIST_ITEM_URL         = "extensions.blocklist.itemURL";
  24. const PREF_BLOCKLIST_ENABLED          = "extensions.blocklist.enabled";
  25. const PREF_BLOCKLIST_INTERVAL         = "extensions.blocklist.interval";
  26. const PREF_BLOCKLIST_LEVEL            = "extensions.blocklist.level";
  27. const PREF_BLOCKLIST_PINGCOUNTTOTAL   = "extensions.blocklist.pingCountTotal";
  28. const PREF_BLOCKLIST_PINGCOUNTVERSION = "extensions.blocklist.pingCountVersion";
  29. const PREF_PLUGINS_NOTIFYUSER         = "plugins.update.notifyUser";
  30. const PREF_GENERAL_USERAGENT_LOCALE   = "general.useragent.locale";
  31. const PREF_PARTNER_BRANCH             = "app.partner.";
  32. const PREF_APP_DISTRIBUTION           = "distribution.id";
  33. const PREF_APP_DISTRIBUTION_VERSION   = "distribution.version";
  34. const PREF_APP_UPDATE_CHANNEL         = "app.update.channel";
  35. const PREF_EM_LOGGING_ENABLED         = "extensions.logging.enabled";
  36. const XMLURI_BLOCKLIST                = "http://www.mozilla.org/2006/addons-blocklist";
  37. const XMLURI_PARSE_ERROR              = "http://www.mozilla.org/newlayout/xml/parsererror.xml"
  38. const UNKNOWN_XPCOM_ABI               = "unknownABI";
  39. const URI_BLOCKLIST_DIALOG            = "chrome://mozapps/content/extensions/blocklist.xul"
  40. const DEFAULT_SEVERITY                = 3;
  41. const DEFAULT_LEVEL                   = 2;
  42. const MAX_BLOCK_LEVEL                 = 3;
  43. const SEVERITY_OUTDATED               = 0;
  44.  
  45. var gLoggingEnabled = null;
  46. var gBlocklistEnabled = true;
  47. var gBlocklistLevel = DEFAULT_LEVEL;
  48.  
  49. XPCOMUtils.defineLazyServiceGetter(this, "gConsole",
  50.                                    "@mozilla.org/consoleservice;1",
  51.                                    "nsIConsoleService");
  52.  
  53. XPCOMUtils.defineLazyServiceGetter(this, "gVersionChecker",
  54.                                    "@mozilla.org/xpcom/version-comparator;1",
  55.                                    "nsIVersionComparator");
  56.  
  57. XPCOMUtils.defineLazyGetter(this, "gPref", function bls_gPref() {
  58.   return Cc["@mozilla.org/preferences-service;1"].getService(Ci.nsIPrefService).
  59.          QueryInterface(Ci.nsIPrefBranch2);
  60. });
  61.  
  62. XPCOMUtils.defineLazyGetter(this, "gApp", function bls_gApp() {
  63.   return Cc["@mozilla.org/xre/app-info;1"].getService(Ci.nsIXULAppInfo).
  64.          QueryInterface(Ci.nsIXULRuntime);
  65. });
  66.  
  67. XPCOMUtils.defineLazyGetter(this, "gABI", function bls_gABI() {
  68.   let abi = null;
  69.   try {
  70.     abi = gApp.XPCOMABI;
  71.   }
  72.   catch (e) {
  73.     LOG("BlockList Global gABI: XPCOM ABI unknown.");
  74.   }
  75. //@line 121 "e:\builds\moz2_slave\rel-m-rel-xr-w32-bld\build\toolkit\mozapps\extensions\nsBlocklistService.js"
  76.   return abi;
  77. });
  78.  
  79. XPCOMUtils.defineLazyGetter(this, "gOSVersion", function bls_gOSVersion() {
  80.   let osVersion;
  81.   let sysInfo = Cc["@mozilla.org/system-info;1"].
  82.                 getService(Ci.nsIPropertyBag2);
  83.   try {
  84.     osVersion = sysInfo.getProperty("name") + " " + sysInfo.getProperty("version");
  85.   }
  86.   catch (e) {
  87.     LOG("BlockList Global gOSVersion: OS Version unknown.");
  88.   }
  89.  
  90.   if (osVersion) {
  91.     try {
  92.       osVersion += " (" + sysInfo.getProperty("secondaryLibrary") + ")";
  93.     }
  94.     catch (e) {
  95.       // Not all platforms have a secondary widget library, so an error is nothing to worry about.
  96.     }
  97.     osVersion = encodeURIComponent(osVersion);
  98.   }
  99.   return osVersion;
  100. });
  101.  
  102. // shared code for suppressing bad cert dialogs
  103. XPCOMUtils.defineLazyGetter(this, "gCertUtils", function bls_gCertUtils() {
  104.   let temp = { };
  105.   Components.utils.import("resource://gre/modules/CertUtils.jsm", temp);
  106.   return temp;
  107. });
  108.  
  109. function getObserverService() {
  110.   return Cc["@mozilla.org/observer-service;1"].getService(Ci.nsIObserverService);
  111. }
  112.  
  113. /**
  114.  * Logs a string to the error console.
  115.  * @param   string
  116.  *          The string to write to the error console..
  117.  */
  118. function LOG(string) {
  119.   if (gLoggingEnabled) {
  120.     dump("*** " + string + "\n");
  121.     gConsole.logStringMessage(string);
  122.   }
  123. }
  124.  
  125. /**
  126.  * Gets a preference value, handling the case where there is no default.
  127.  * @param   func
  128.  *          The name of the preference function to call, on nsIPrefBranch
  129.  * @param   preference
  130.  *          The name of the preference
  131.  * @param   defaultValue
  132.  *          The default value to return in the event the preference has
  133.  *          no setting
  134.  * @returns The value of the preference, or undefined if there was no
  135.  *          user or default value.
  136.  */
  137. function getPref(func, preference, defaultValue) {
  138.   try {
  139.     return gPref[func](preference);
  140.   }
  141.   catch (e) {
  142.   }
  143.   return defaultValue;
  144. }
  145.  
  146. /**
  147.  * Constructs a URI to a spec.
  148.  * @param   spec
  149.  *          The spec to construct a URI to
  150.  * @returns The nsIURI constructed.
  151.  */
  152. function newURI(spec) {
  153.   var ioServ = Cc["@mozilla.org/network/io-service;1"].
  154.                getService(Ci.nsIIOService);
  155.   return ioServ.newURI(spec, null, null);
  156. }
  157.  
  158. // Restarts the application checking in with observers first
  159. function restartApp() {
  160.   // Notify all windows that an application quit has been requested.
  161.   var os = Cc["@mozilla.org/observer-service;1"].
  162.            getService(Ci.nsIObserverService);
  163.   var cancelQuit = Cc["@mozilla.org/supports-PRBool;1"].
  164.                    createInstance(Ci.nsISupportsPRBool);
  165.   os.notifyObservers(cancelQuit, "quit-application-requested", null);
  166.  
  167.   // Something aborted the quit process.
  168.   if (cancelQuit.data)
  169.     return;
  170.  
  171.   var as = Cc["@mozilla.org/toolkit/app-startup;1"].
  172.            getService(Ci.nsIAppStartup);
  173.   as.quit(Ci.nsIAppStartup.eRestart | Ci.nsIAppStartup.eAttemptQuit);
  174. }
  175.  
  176. /**
  177.  * Checks whether this blocklist element is valid for the current OS and ABI.
  178.  * If the element has an "os" attribute then the current OS must appear in
  179.  * its comma separated list for the element to be valid. Similarly for the
  180.  * xpcomabi attribute.
  181.  */
  182. function matchesOSABI(blocklistElement) {
  183.   if (blocklistElement.hasAttribute("os")) {
  184.     var choices = blocklistElement.getAttribute("os").split(",");
  185.     if (choices.length > 0 && choices.indexOf(gApp.OS) < 0)
  186.       return false;
  187.   }
  188.  
  189.   if (blocklistElement.hasAttribute("xpcomabi")) {
  190.     choices = blocklistElement.getAttribute("xpcomabi").split(",");
  191.     if (choices.length > 0 && choices.indexOf(gApp.XPCOMABI) < 0)
  192.       return false;
  193.   }
  194.  
  195.   return true;
  196. }
  197.  
  198. /**
  199.  * Gets the current value of the locale.  It's possible for this preference to
  200.  * be localized, so we have to do a little extra work here.  Similar code
  201.  * exists in nsHttpHandler.cpp when building the UA string.
  202.  */
  203. function getLocale() {
  204.   try {
  205.       // Get the default branch
  206.       var defaultPrefs = gPref.getDefaultBranch(null);
  207.       return defaultPrefs.getComplexValue(PREF_GENERAL_USERAGENT_LOCALE,
  208.                                           Ci.nsIPrefLocalizedString).data;
  209.   } catch (e) {}
  210.  
  211.   return gPref.getCharPref(PREF_GENERAL_USERAGENT_LOCALE);
  212. }
  213.  
  214. /**
  215.  * Read the update channel from defaults only.  We do this to ensure that
  216.  * the channel is tightly coupled with the application and does not apply
  217.  * to other installations of the application that may use the same profile.
  218.  */
  219. function getUpdateChannel() {
  220.   var channel = "default";
  221.   var prefName;
  222.   var prefValue;
  223.  
  224.   var defaults = gPref.getDefaultBranch(null);
  225.   try {
  226.     channel = defaults.getCharPref(PREF_APP_UPDATE_CHANNEL);
  227.   } catch (e) {
  228.     // use default when pref not found
  229.   }
  230.  
  231.   try {
  232.     var partners = gPref.getChildList(PREF_PARTNER_BRANCH);
  233.     if (partners.length) {
  234.       channel += "-cck";
  235.       partners.sort();
  236.  
  237.       for each (prefName in partners) {
  238.         prefValue = gPref.getCharPref(prefName);
  239.         channel += "-" + prefValue;
  240.       }
  241.     }
  242.   }
  243.   catch (e) {
  244.     Components.utils.reportError(e);
  245.   }
  246.  
  247.   return channel;
  248. }
  249.  
  250. /* Get the distribution pref values, from defaults only */
  251. function getDistributionPrefValue(aPrefName) {
  252.   var prefValue = "default";
  253.  
  254.   var defaults = gPref.getDefaultBranch(null);
  255.   try {
  256.     prefValue = defaults.getCharPref(aPrefName);
  257.   } catch (e) {
  258.     // use default when pref not found
  259.   }
  260.  
  261.   return prefValue;
  262. }
  263.  
  264. /**
  265.  * Manages the Blocklist. The Blocklist is a representation of the contents of
  266.  * blocklist.xml and allows us to remotely disable / re-enable blocklisted
  267.  * items managed by the Extension Manager with an item's appDisabled property.
  268.  * It also blocklists plugins with data from blocklist.xml.
  269.  */
  270.  
  271. function Blocklist() {
  272.   let os = getObserverService();
  273.   os.addObserver(this, "xpcom-shutdown", false);
  274.   gLoggingEnabled = getPref("getBoolPref", PREF_EM_LOGGING_ENABLED, false);
  275.   gBlocklistEnabled = getPref("getBoolPref", PREF_BLOCKLIST_ENABLED, true);
  276.   gBlocklistLevel = Math.min(getPref("getIntPref", PREF_BLOCKLIST_LEVEL, DEFAULT_LEVEL),
  277.                                      MAX_BLOCK_LEVEL);
  278.   gPref.addObserver("extensions.blocklist.", this, false);
  279. }
  280.  
  281. Blocklist.prototype = {
  282.   /**
  283.    * Extension ID -> array of Version Ranges
  284.    * Each value in the version range array is a JS Object that has the
  285.    * following properties:
  286.    *   "minVersion"  The minimum version in a version range (default = 0)
  287.    *   "maxVersion"  The maximum version in a version range (default = *)
  288.    *   "targetApps"  Application ID -> array of Version Ranges
  289.    *                 (default = current application ID)
  290.    *                 Each value in the version range array is a JS Object that
  291.    *                 has the following properties:
  292.    *                   "minVersion"  The minimum version in a version range
  293.    *                                 (default = 0)
  294.    *                   "maxVersion"  The maximum version in a version range
  295.    *                                 (default = *)
  296.    */
  297.   _addonEntries: null,
  298.   _pluginEntries: null,
  299.  
  300.   observe: function(aSubject, aTopic, aData) {
  301.     switch (aTopic) {
  302.     case "xpcom-shutdown":
  303.       let os = getObserverService();
  304.       os.removeObserver(this, "xpcom-shutdown");
  305.       gPref.removeObserver("extensions.blocklist.", this);
  306.       break;
  307.     case "nsPref:changed":
  308.       switch (aData) {
  309.         case PREF_BLOCKLIST_ENABLED:
  310.           gBlocklistEnabled = getPref("getBoolPref", PREF_BLOCKLIST_ENABLED, true);
  311.           this._loadBlocklist();
  312.           this._blocklistUpdated(null, null);
  313.           break;
  314.         case PREF_BLOCKLIST_LEVEL:
  315.           gBlocklistLevel = Math.min(getPref("getIntPref", PREF_BLOCKLIST_LEVEL, DEFAULT_LEVEL),
  316.                                      MAX_BLOCK_LEVEL);
  317.           this._blocklistUpdated(null, null);
  318.           break;
  319.       }
  320.       break;
  321.     }
  322.   },
  323.  
  324.   /* See nsIBlocklistService */
  325.   isAddonBlocklisted: function(id, version, appVersion, toolkitVersion) {
  326.     return this.getAddonBlocklistState(id, version, appVersion, toolkitVersion) ==
  327.                    Ci.nsIBlocklistService.STATE_BLOCKED;
  328.   },
  329.  
  330.   /* See nsIBlocklistService */
  331.   getAddonBlocklistState: function(id, version, appVersion, toolkitVersion) {
  332.     if (!this._addonEntries)
  333.       this._loadBlocklist();
  334.     return this._getAddonBlocklistState(id, version, this._addonEntries,
  335.                                         appVersion, toolkitVersion);
  336.   },
  337.  
  338.   /**
  339.    * Private version of getAddonBlocklistState that allows the caller to pass in
  340.    * the add-on blocklist entries to compare against.
  341.    *
  342.    * @param   id
  343.    *          The ID of the item to get the blocklist state for.
  344.    * @param   version
  345.    *          The version of the item to get the blocklist state for.
  346.    * @param   addonEntries
  347.    *          The add-on blocklist entries to compare against.
  348.    * @param   appVersion
  349.    *          The application version to compare to, will use the current
  350.    *          version if null.
  351.    * @param   toolkitVersion
  352.    *          The toolkit version to compare to, will use the current version if
  353.    *          null.
  354.    * @returns The blocklist state for the item, one of the STATE constants as
  355.    *          defined in nsIBlocklistService.
  356.    */
  357.   _getAddonBlocklistState: function(id, version, addonEntries, appVersion, toolkitVersion) {
  358.     if (!gBlocklistEnabled)
  359.       return Ci.nsIBlocklistService.STATE_NOT_BLOCKED;
  360.  
  361.     if (!appVersion)
  362.       appVersion = gApp.version;
  363.     if (!toolkitVersion)
  364.       toolkitVersion = gApp.platformVersion;
  365.  
  366.     var blItem = addonEntries[id];
  367.     if (!blItem)
  368.       return Ci.nsIBlocklistService.STATE_NOT_BLOCKED;
  369.  
  370.     for (var i = 0; i < blItem.length; ++i) {
  371.       if (blItem[i].includesItem(version, appVersion, toolkitVersion))
  372.         return blItem[i].severity >= gBlocklistLevel ? Ci.nsIBlocklistService.STATE_BLOCKED :
  373.                                                        Ci.nsIBlocklistService.STATE_SOFTBLOCKED;
  374.     }
  375.     return Ci.nsIBlocklistService.STATE_NOT_BLOCKED;
  376.   },
  377.  
  378.   /* See nsIBlocklistService */
  379.   getAddonBlocklistURL: function(id, version, appVersion, toolkitVersion) {
  380.     if (!gBlocklistEnabled)
  381.       return "";
  382.  
  383.     if (!this._addonEntries)
  384.       this._loadBlocklist();
  385.  
  386.     let blItem = this._addonEntries[id];
  387.     if (!blItem || !blItem.blockID)
  388.       return null;
  389.  
  390.     return this._createBlocklistURL(blItem.blockID);
  391.   },
  392.  
  393.   _createBlocklistURL: function(id) {
  394.     let url = Services.urlFormatter.formatURLPref(PREF_BLOCKLIST_ITEM_URL);
  395.     url = url.replace(/%blockID%/g, id);
  396.  
  397.     return url;
  398.   },
  399.  
  400.   notify: function(aTimer) {
  401.     if (!gBlocklistEnabled)
  402.       return;
  403.  
  404.     try {
  405.       var dsURI = gPref.getCharPref(PREF_BLOCKLIST_URL);
  406.     }
  407.     catch (e) {
  408.       LOG("Blocklist::notify: The " + PREF_BLOCKLIST_URL + " preference" +
  409.           " is missing!");
  410.       return;
  411.     }
  412.  
  413.     var pingCountVersion = getPref("getIntPref", PREF_BLOCKLIST_PINGCOUNTVERSION, 0);
  414.     var pingCountTotal = getPref("getIntPref", PREF_BLOCKLIST_PINGCOUNTTOTAL, 1);
  415.     var daysSinceLastPing = 0;
  416.     if (pingCountVersion == 0) {
  417.       daysSinceLastPing = "new";
  418.     }
  419.     else {
  420.       // Seconds in one day is used because nsIUpdateTimerManager stores the
  421.       // last update time in seconds.
  422.       let secondsInDay = 60 * 60 * 24;
  423.       let lastUpdateTime = getPref("getIntPref", PREF_BLOCKLIST_LASTUPDATETIME, 0);
  424.       if (lastUpdateTime == 0) {
  425.         daysSinceLastPing = "invalid";
  426.       }
  427.       else {
  428.         let now = Math.round(Date.now() / 1000);
  429.         daysSinceLastPing = Math.floor((now - lastUpdateTime) / secondsInDay);
  430.       }
  431.  
  432.       if (daysSinceLastPing == 0 || daysSinceLastPing == "invalid") {
  433.         pingCountVersion = pingCountTotal = "invalid";
  434.       }
  435.     }
  436.  
  437.     if (pingCountVersion < 1)
  438.       pingCountVersion = 1;
  439.     if (pingCountTotal < 1)
  440.       pingCountTotal = 1;
  441.  
  442.     dsURI = dsURI.replace(/%APP_ID%/g, gApp.ID);
  443.     dsURI = dsURI.replace(/%APP_VERSION%/g, gApp.version);
  444.     dsURI = dsURI.replace(/%PRODUCT%/g, gApp.name);
  445.     dsURI = dsURI.replace(/%VERSION%/g, gApp.version);
  446.     dsURI = dsURI.replace(/%BUILD_ID%/g, gApp.appBuildID);
  447.     dsURI = dsURI.replace(/%BUILD_TARGET%/g, gApp.OS + "_" + gABI);
  448.     dsURI = dsURI.replace(/%OS_VERSION%/g, gOSVersion);
  449.     dsURI = dsURI.replace(/%LOCALE%/g, getLocale());
  450.     dsURI = dsURI.replace(/%CHANNEL%/g, getUpdateChannel());
  451.     dsURI = dsURI.replace(/%PLATFORM_VERSION%/g, gApp.platformVersion);
  452.     dsURI = dsURI.replace(/%DISTRIBUTION%/g,
  453.                       getDistributionPrefValue(PREF_APP_DISTRIBUTION));
  454.     dsURI = dsURI.replace(/%DISTRIBUTION_VERSION%/g,
  455.                       getDistributionPrefValue(PREF_APP_DISTRIBUTION_VERSION));
  456.     dsURI = dsURI.replace(/%PING_COUNT%/g, pingCountVersion);
  457.     dsURI = dsURI.replace(/%TOTAL_PING_COUNT%/g, pingCountTotal);
  458.     dsURI = dsURI.replace(/%DAYS_SINCE_LAST_PING%/g, daysSinceLastPing);
  459.     dsURI = dsURI.replace(/\+/g, "%2B");
  460.  
  461.     // Under normal operations it will take around 5,883,516 years before the
  462.     // preferences used to store pingCountVersion and pingCountTotal will rollover
  463.     // so this code doesn't bother trying to do the "right thing" here.
  464.     if (pingCountVersion != "invalid") {
  465.       pingCountVersion++;
  466.       if (pingCountVersion > 2147483647) {
  467.         // Rollover to -1 if the value is greater than what is support by an
  468.         // integer preference. The -1 indicates that the counter has been reset.
  469.         pingCountVersion = -1;
  470.       }
  471.       gPref.setIntPref(PREF_BLOCKLIST_PINGCOUNTVERSION, pingCountVersion);
  472.     }
  473.  
  474.     if (pingCountTotal != "invalid") {
  475.       pingCountTotal++;
  476.       if (pingCountTotal > 2147483647) {
  477.         // Rollover to 1 if the value is greater than what is support by an
  478.         // integer preference.
  479.         pingCountTotal = -1;
  480.       }
  481.       gPref.setIntPref(PREF_BLOCKLIST_PINGCOUNTTOTAL, pingCountTotal);
  482.     }
  483.  
  484.     // Verify that the URI is valid
  485.     try {
  486.       var uri = newURI(dsURI);
  487.     }
  488.     catch (e) {
  489.       LOG("Blocklist::notify: There was an error creating the blocklist URI\r\n" +
  490.           "for: " + dsURI + ", error: " + e);
  491.       return;
  492.     }
  493.  
  494.     LOG("Blocklist::notify: Requesting " + uri.spec);
  495.     var request = Cc["@mozilla.org/xmlextras/xmlhttprequest;1"].
  496.                   createInstance(Ci.nsIXMLHttpRequest);
  497.     request.open("GET", uri.spec, true);
  498.     request.channel.notificationCallbacks = new gCertUtils.BadCertHandler();
  499.     request.overrideMimeType("text/xml");
  500.     request.setRequestHeader("Cache-Control", "no-cache");
  501.     request.QueryInterface(Components.interfaces.nsIJSXMLHttpRequest);
  502.  
  503.     var self = this;
  504.     request.addEventListener("error", function(event) { self.onXMLError(event); }, false);
  505.     request.addEventListener("load", function(event) { self.onXMLLoad(event);  }, false);
  506.     request.send(null);
  507.  
  508.     // When the blocklist loads we need to compare it to the current copy so
  509.     // make sure we have loaded it.
  510.     if (!this._addonEntries)
  511.       this._loadBlocklist();
  512.   },
  513.  
  514.   onXMLLoad: function(aEvent) {
  515.     var request = aEvent.target;
  516.     try {
  517.       gCertUtils.checkCert(request.channel);
  518.     }
  519.     catch (e) {
  520.       LOG("Blocklist::onXMLLoad: " + e);
  521.       return;
  522.     }
  523.     var responseXML = request.responseXML;
  524.     if (!responseXML || responseXML.documentElement.namespaceURI == XMLURI_PARSE_ERROR ||
  525.         (request.status != 200 && request.status != 0)) {
  526.       LOG("Blocklist::onXMLLoad: there was an error during load");
  527.       return;
  528.     }
  529.     var blocklistFile = FileUtils.getFile(KEY_PROFILEDIR, [FILE_BLOCKLIST]);
  530.     if (blocklistFile.exists())
  531.       blocklistFile.remove(false);
  532.     var fos = FileUtils.openSafeFileOutputStream(blocklistFile);
  533.     fos.write(request.responseText, request.responseText.length);
  534.     FileUtils.closeSafeFileOutputStream(fos);
  535.  
  536.     var oldAddonEntries = this._addonEntries;
  537.     var oldPluginEntries = this._pluginEntries;
  538.     this._addonEntries = { };
  539.     this._pluginEntries = { };
  540.     this._loadBlocklistFromFile(FileUtils.getFile(KEY_PROFILEDIR,
  541.                                                   [FILE_BLOCKLIST]));
  542.  
  543.     this._blocklistUpdated(oldAddonEntries, oldPluginEntries);
  544.   },
  545.  
  546.   onXMLError: function(aEvent) {
  547.     try {
  548.       var request = aEvent.target;
  549.       // the following may throw (e.g. a local file or timeout)
  550.       var status = request.status;
  551.     }
  552.     catch (e) {
  553.       request = aEvent.target.channel.QueryInterface(Ci.nsIRequest);
  554.       status = request.status;
  555.     }
  556.     var statusText = "nsIXMLHttpRequest channel unavailable";
  557.     // When status is 0 we don't have a valid channel.
  558.     if (status != 0) {
  559.       try {
  560.         statusText = request.statusText;
  561.       } catch (e) {
  562.       }
  563.     }
  564.     LOG("Blocklist:onError: There was an error loading the blocklist file\r\n" +
  565.         statusText);
  566.   },
  567.  
  568.   /**
  569.    * Finds the newest blocklist file from the application and the profile and
  570.    * load it or does nothing if neither exist.
  571.    */
  572.   _loadBlocklist: function() {
  573.     this._addonEntries = { };
  574.     this._pluginEntries = { };
  575.     var profFile = FileUtils.getFile(KEY_PROFILEDIR, [FILE_BLOCKLIST]);
  576.     if (profFile.exists()) {
  577.       this._loadBlocklistFromFile(profFile);
  578.       return;
  579.     }
  580.     var appFile = FileUtils.getFile(KEY_APPDIR, [FILE_BLOCKLIST]);
  581.     if (appFile.exists()) {
  582.       this._loadBlocklistFromFile(appFile);
  583.       return;
  584.     }
  585.     LOG("Blocklist::_loadBlocklist: no XML File found");
  586.   },
  587.  
  588.   /**
  589. //@line 683 "e:\builds\moz2_slave\rel-m-rel-xr-w32-bld\build\toolkit\mozapps\extensions\nsBlocklistService.js"
  590.    */
  591.  
  592.   _loadBlocklistFromFile: function(file) {
  593.     if (!gBlocklistEnabled) {
  594.       LOG("Blocklist::_loadBlocklistFromFile: blocklist is disabled");
  595.       return;
  596.     }
  597.  
  598.     if (!file.exists()) {
  599.       LOG("Blocklist::_loadBlocklistFromFile: XML File does not exist");
  600.       return;
  601.     }
  602.  
  603.     var fileStream = Components.classes["@mozilla.org/network/file-input-stream;1"]
  604.                                .createInstance(Components.interfaces.nsIFileInputStream);
  605.     fileStream.init(file, FileUtils.MODE_RDONLY, FileUtils.PERMS_FILE, 0);
  606.     try {
  607.       var parser = Cc["@mozilla.org/xmlextras/domparser;1"].
  608.                    createInstance(Ci.nsIDOMParser);
  609.       var doc = parser.parseFromStream(fileStream, "UTF-8", file.fileSize, "text/xml");
  610.       if (doc.documentElement.namespaceURI != XMLURI_BLOCKLIST) {
  611.         LOG("Blocklist::_loadBlocklistFromFile: aborting due to incorrect " +
  612.             "XML Namespace.\r\nExpected: " + XMLURI_BLOCKLIST + "\r\n" +
  613.             "Received: " + doc.documentElement.namespaceURI);
  614.         return;
  615.       }
  616.  
  617.       var childNodes = doc.documentElement.childNodes;
  618.       for (var i = 0; i < childNodes.length; ++i) {
  619.         var element = childNodes[i];
  620.         if (!(element instanceof Ci.nsIDOMElement))
  621.           continue;
  622.         switch (element.localName) {
  623.         case "emItems":
  624.           this._addonEntries = this._processItemNodes(element.childNodes, "em",
  625.                                                       this._handleEmItemNode);
  626.           break;
  627.         case "pluginItems":
  628.           this._pluginEntries = this._processItemNodes(element.childNodes, "plugin",
  629.                                                        this._handlePluginItemNode);
  630.           break;
  631.         default:
  632.           Services.obs.notifyObservers(element,
  633.                                        "blocklist-data-" + element.localName,
  634.                                        null);
  635.         }
  636.       }
  637.     }
  638.     catch (e) {
  639.       LOG("Blocklist::_loadBlocklistFromFile: Error constructing blocklist " + e);
  640.       return;
  641.     }
  642.     fileStream.close();
  643.   },
  644.  
  645.   _processItemNodes: function(itemNodes, prefix, handler) {
  646.     var result = [];
  647.     var itemName = prefix + "Item";
  648.     for (var i = 0; i < itemNodes.length; ++i) {
  649.       var blocklistElement = itemNodes.item(i);
  650.       if (!(blocklistElement instanceof Ci.nsIDOMElement) ||
  651.           blocklistElement.localName != itemName)
  652.         continue;
  653.  
  654.       handler(blocklistElement, result);
  655.     }
  656.     return result;
  657.   },
  658.  
  659.   _handleEmItemNode: function(blocklistElement, result) {
  660.     if (!matchesOSABI(blocklistElement))
  661.       return;
  662.  
  663.     var versionNodes = blocklistElement.childNodes;
  664.     var id = blocklistElement.getAttribute("id");
  665.     result[id] = [];
  666.     for (var x = 0; x < versionNodes.length; ++x) {
  667.       var versionRangeElement = versionNodes.item(x);
  668.       if (!(versionRangeElement instanceof Ci.nsIDOMElement) ||
  669.           versionRangeElement.localName != "versionRange")
  670.         continue;
  671.  
  672.       result[id].push(new BlocklistItemData(versionRangeElement));
  673.     }
  674.     // if only the extension ID is specified block all versions of the
  675.     // extension for the current application.
  676.     if (result[id].length == 0)
  677.       result[id].push(new BlocklistItemData(null));
  678.  
  679.     result[id].blockID = blocklistElement.getAttribute("blockID");
  680.   },
  681.  
  682.   _handlePluginItemNode: function(blocklistElement, result) {
  683.     if (!matchesOSABI(blocklistElement))
  684.       return;
  685.  
  686.     var matchNodes = blocklistElement.childNodes;
  687.     var blockEntry = {
  688.       matches: {},
  689.       versions: [],
  690.       blockID: null,
  691.     };
  692.     var hasMatch = false;
  693.     for (var x = 0; x < matchNodes.length; ++x) {
  694.       var matchElement = matchNodes.item(x);
  695.       if (!(matchElement instanceof Ci.nsIDOMElement))
  696.         continue;
  697.       if (matchElement.localName == "match") {
  698.         var name = matchElement.getAttribute("name");
  699.         var exp = matchElement.getAttribute("exp");
  700.         try {
  701.           blockEntry.matches[name] = new RegExp(exp, "m");
  702.           hasMatch = true;
  703.         } catch (e) {
  704.           // Ignore invalid regular expressions
  705.         }
  706.       }
  707.       if (matchElement.localName == "versionRange")
  708.         blockEntry.versions.push(new BlocklistItemData(matchElement));
  709.     }
  710.     // Plugin entries require *something* to match to an actual plugin
  711.     if (!hasMatch)
  712.       return;
  713.     // Add a default versionRange if there wasn't one specified
  714.     if (blockEntry.versions.length == 0)
  715.       blockEntry.versions.push(new BlocklistItemData(null));
  716.  
  717.     blockEntry.blockID = blocklistElement.getAttribute("blockID");
  718.  
  719.     result.push(blockEntry);
  720.   },
  721.  
  722.   /* See nsIBlocklistService */
  723.   getPluginBlocklistState: function(plugin, appVersion, toolkitVersion) {
  724.     if (!this._pluginEntries)
  725.       this._loadBlocklist();
  726.     return this._getPluginBlocklistState(plugin, this._pluginEntries,
  727.                                          appVersion, toolkitVersion);
  728.   },
  729.  
  730.   /**
  731.    * Private version of getPluginBlocklistState that allows the caller to pass in
  732.    * the plugin blocklist entries.
  733.    *
  734.    * @param   plugin
  735.    *          The nsIPluginTag to get the blocklist state for.
  736.    * @param   pluginEntries
  737.    *          The plugin blocklist entries to compare against.
  738.    * @param   appVersion
  739.    *          The application version to compare to, will use the current
  740.    *          version if null.
  741.    * @param   toolkitVersion
  742.    *          The toolkit version to compare to, will use the current version if
  743.    *          null.
  744.    * @returns The blocklist state for the item, one of the STATE constants as
  745.    *          defined in nsIBlocklistService.
  746.    */
  747.   _getPluginBlocklistState: function(plugin, pluginEntries, appVersion, toolkitVersion) {
  748.     if (!gBlocklistEnabled)
  749.       return Ci.nsIBlocklistService.STATE_NOT_BLOCKED;
  750.  
  751.     if (!appVersion)
  752.       appVersion = gApp.version;
  753.     if (!toolkitVersion)
  754.       toolkitVersion = gApp.platformVersion;
  755.  
  756.     for each (var blockEntry in pluginEntries) {
  757.       var matchFailed = false;
  758.       for (var name in blockEntry.matches) {
  759.         if (!(name in plugin) ||
  760.             typeof(plugin[name]) != "string" ||
  761.             !blockEntry.matches[name].test(plugin[name])) {
  762.           matchFailed = true;
  763.           break;
  764.         }
  765.       }
  766.  
  767.       if (matchFailed)
  768.         continue;
  769.  
  770.       for (var i = 0; i < blockEntry.versions.length; i++) {
  771.         if (blockEntry.versions[i].includesItem(plugin.version, appVersion,
  772.                                                 toolkitVersion)) {
  773.           if (blockEntry.versions[i].severity >= gBlocklistLevel)
  774.             return Ci.nsIBlocklistService.STATE_BLOCKED;
  775.           if (blockEntry.versions[i].severity == SEVERITY_OUTDATED)
  776.             return Ci.nsIBlocklistService.STATE_OUTDATED;
  777.           return Ci.nsIBlocklistService.STATE_SOFTBLOCKED;
  778.         }
  779.       }
  780.     }
  781.  
  782.     return Ci.nsIBlocklistService.STATE_NOT_BLOCKED;
  783.   },
  784.  
  785.   /* See nsIBlocklistService */
  786.   getPluginBlocklistURL: function(plugin) {
  787.     if (!gBlocklistEnabled)
  788.       return "";
  789.  
  790.     if (!this._pluginEntries)
  791.       this._loadBlocklist();
  792.  
  793.     for each (let blockEntry in this._pluginEntries) {
  794.       let matchFailed = false;
  795.       for (let name in blockEntry.matches) {
  796.         if (!(name in plugin) ||
  797.             typeof(plugin[name]) != "string" ||
  798.             !blockEntry.matches[name].test(plugin[name])) {
  799.           matchFailed = true;
  800.           break;
  801.         }
  802.       }
  803.  
  804.       if (!matchFailed) {
  805.         if(!blockEntry.blockID)
  806.           return null;
  807.         else
  808.           return this._createBlocklistURL(blockEntry.blockID);
  809.       }
  810.     }
  811.   },
  812.  
  813.   _blocklistUpdated: function(oldAddonEntries, oldPluginEntries) {
  814.     var addonList = [];
  815.  
  816.     var self = this;
  817.     AddonManager.getAddonsByTypes(["extension", "theme", "locale", "dictionary"], function(addons) {
  818.  
  819.       for (let i = 0; i < addons.length; i++) {
  820.         let oldState = Ci.nsIBlocklistService.STATE_NOTBLOCKED;
  821.         if (oldAddonEntries)
  822.           oldState = self._getAddonBlocklistState(addons[i].id, addons[i].version,
  823.                                                   oldAddonEntries);
  824.         let state = self.getAddonBlocklistState(addons[i].id, addons[i].version);
  825.  
  826.         LOG("Blocklist state for " + addons[i].id + " changed from " +
  827.             oldState + " to " + state);
  828.  
  829.         // We don't want to re-warn about add-ons
  830.         if (state == oldState)
  831.           continue;
  832.  
  833.         // Ensure that softDisabled is false if the add-on is not soft blocked
  834.         if (state != Ci.nsIBlocklistService.STATE_SOFTBLOCKED)
  835.           addons[i].softDisabled = false;
  836.  
  837.         // Don't warn about add-ons becoming unblocked.
  838.         if (state == Ci.nsIBlocklistService.STATE_NOT_BLOCKED)
  839.           continue;
  840.  
  841.         // If an add-on has dropped from hard to soft blocked just mark it as
  842.         // soft disabled and don't warn about it.
  843.         if (state == Ci.nsIBlocklistService.STATE_SOFTBLOCKED &&
  844.             oldState == Ci.nsIBlocklistService.STATE_BLOCKED) {
  845.           addons[i].softDisabled = true;
  846.           continue;
  847.         }
  848.  
  849.         // If the add-on is already disabled for some reason then don't warn
  850.         // about it
  851.         if (!addons[i].isActive)
  852.           continue;
  853.  
  854.         addonList.push({
  855.           name: addons[i].name,
  856.           version: addons[i].version,
  857.           icon: addons[i].iconURL,
  858.           disable: false,
  859.           blocked: state == Ci.nsIBlocklistService.STATE_BLOCKED,
  860.           item: addons[i],
  861.           url: self.getAddonBlocklistURL(addons[i].id),
  862.         });
  863.       }
  864.  
  865.       AddonManagerPrivate.updateAddonAppDisabledStates();
  866.  
  867.       var phs = Cc["@mozilla.org/plugin/host;1"].
  868.                 getService(Ci.nsIPluginHost);
  869.       var plugins = phs.getPluginTags();
  870.  
  871.       for (let i = 0; i < plugins.length; i++) {
  872.         let oldState = -1;
  873.         if (oldPluginEntries)
  874.           oldState = self._getPluginBlocklistState(plugins[i], oldPluginEntries);
  875.         let state = self.getPluginBlocklistState(plugins[i]);
  876.         LOG("Blocklist state for " + plugins[i].name + " changed from " +
  877.             oldState + " to " + state);
  878.         // We don't want to re-warn about items
  879.         if (state == oldState)
  880.           continue;
  881.  
  882.         if (plugins[i].blocklisted) {
  883.           if (state == Ci.nsIBlocklistService.STATE_SOFTBLOCKED)
  884.             plugins[i].disabled = true;
  885.         }
  886.         else if (!plugins[i].disabled && state != Ci.nsIBlocklistService.STATE_NOT_BLOCKED) {
  887.           if (state == Ci.nsIBlocklistService.STATE_OUTDATED) {
  888.             gPref.setBoolPref(PREF_PLUGINS_NOTIFYUSER, true);
  889.           }
  890.           else {
  891.             addonList.push({
  892.               name: plugins[i].name,
  893.               version: plugins[i].version,
  894.               icon: "chrome://mozapps/skin/plugins/pluginGeneric.png",
  895.               disable: false,
  896.               blocked: state == Ci.nsIBlocklistService.STATE_BLOCKED,
  897.               item: plugins[i],
  898.               url: self.getPluginBlocklistURL(plugins[i]),
  899.             });
  900.           }
  901.         }
  902.         plugins[i].blocklisted = state == Ci.nsIBlocklistService.STATE_BLOCKED;
  903.       }
  904.  
  905.       if (addonList.length == 0) {
  906.         Services.obs.notifyObservers(self, "blocklist-updated", "");
  907.         return;
  908.       }
  909.  
  910.       if ("@mozilla.org/addons/blocklist-prompt;1" in Cc) {
  911.         try {
  912.           let blockedPrompter = Cc["@mozilla.org/addons/blocklist-prompt;1"]
  913.                                  .getService(Ci.nsIBlocklistPrompt);
  914.           blockedPrompter.prompt(addonList);
  915.         } catch (e) {
  916.           LOG(e);
  917.         }
  918.         Services.obs.notifyObservers(self, "blocklist-updated", "");
  919.         return;
  920.       }
  921.  
  922.       var args = {
  923.         restart: false,
  924.         list: addonList
  925.       };
  926.       // This lets the dialog get the raw js object
  927.       args.wrappedJSObject = args;
  928.  
  929.       /*
  930.         Some tests run without UI, so the async code listens to a message
  931.         that can be sent programatically
  932.       */
  933.       let applyBlocklistChanges = function() {
  934.         for (let i = 0; i < addonList.length; i++) {
  935.           if (!addonList[i].disable)
  936.             continue;
  937.  
  938.           if (addonList[i].item instanceof Ci.nsIPluginTag)
  939.             addonList[i].item.disabled = true;
  940.           else
  941.             addonList[i].item.softDisabled = true;
  942.         }
  943.  
  944.         if (args.restart)
  945.           restartApp();
  946.  
  947.         Services.obs.notifyObservers(self, "blocklist-updated", "");
  948.         Services.obs.removeObserver(applyBlocklistChanges, "addon-blocklist-closed");
  949.       }
  950.  
  951.       Services.obs.addObserver(applyBlocklistChanges, "addon-blocklist-closed", false)
  952.  
  953.       function blocklistUnloadHandler(event) {
  954.         if (event.target.location == URI_BLOCKLIST_DIALOG) {
  955.           applyBlocklistChanges();
  956.           blocklistWindow.removeEventListener("unload", blocklistUnloadHandler);
  957.         }
  958.       }
  959.  
  960.       let blocklistWindow = Services.ww.openWindow(null, URI_BLOCKLIST_DIALOG, "",
  961.                               "chrome,centerscreen,dialog,titlebar", args);
  962.       blocklistWindow.addEventListener("unload", blocklistUnloadHandler, false);
  963.     });
  964.   },
  965.  
  966.   classID: Components.ID("{66354bc9-7ed1-4692-ae1d-8da97d6b205e}"),
  967.   QueryInterface: XPCOMUtils.generateQI([Ci.nsIObserver,
  968.                                          Ci.nsIBlocklistService,
  969.                                          Ci.nsITimerCallback]),
  970. };
  971.  
  972. /**
  973.  * Helper for constructing a blocklist.
  974.  */
  975. function BlocklistItemData(versionRangeElement) {
  976.   var versionRange = this.getBlocklistVersionRange(versionRangeElement);
  977.   this.minVersion = versionRange.minVersion;
  978.   this.maxVersion = versionRange.maxVersion;
  979.   if (versionRangeElement && versionRangeElement.hasAttribute("severity"))
  980.     this.severity = versionRangeElement.getAttribute("severity");
  981.   else
  982.     this.severity = DEFAULT_SEVERITY;
  983.   this.targetApps = { };
  984.   var found = false;
  985.  
  986.   if (versionRangeElement) {
  987.     for (var i = 0; i < versionRangeElement.childNodes.length; ++i) {
  988.       var targetAppElement = versionRangeElement.childNodes.item(i);
  989.       if (!(targetAppElement instanceof Ci.nsIDOMElement) ||
  990.           targetAppElement.localName != "targetApplication")
  991.         continue;
  992.       found = true;
  993.       // default to the current application if id is not provided.
  994.       var appID = targetAppElement.hasAttribute("id") ? targetAppElement.getAttribute("id") : gApp.ID;
  995.       this.targetApps[appID] = this.getBlocklistAppVersions(targetAppElement);
  996.     }
  997.   }
  998.   // Default to all versions of the current application when no targetApplication
  999.   // elements were found
  1000.   if (!found)
  1001.     this.targetApps[gApp.ID] = this.getBlocklistAppVersions(null);
  1002. }
  1003.  
  1004. BlocklistItemData.prototype = {
  1005.   /**
  1006.    * Tests if a version of an item is included in the version range and target
  1007.    * application information represented by this BlocklistItemData using the
  1008.    * provided application and toolkit versions.
  1009.    * @param   version
  1010.    *          The version of the item being tested.
  1011.    * @param   appVersion
  1012.    *          The application version to test with.
  1013.    * @param   toolkitVersion
  1014.    *          The toolkit version to test with.
  1015.    * @returns True if the version range covers the item version and application
  1016.    *          or toolkit version.
  1017.    */
  1018.   includesItem: function(version, appVersion, toolkitVersion) {
  1019.     // Some platforms have no version for plugins, these don't match if there
  1020.     // was a min/maxVersion provided
  1021.     if (!version && (this.minVersion || this.maxVersion))
  1022.       return false;
  1023.  
  1024.     // Check if the item version matches
  1025.     if (!this.matchesRange(version, this.minVersion, this.maxVersion))
  1026.       return false;
  1027.  
  1028.     // Check if the application version matches
  1029.     if (this.matchesTargetRange(gApp.ID, appVersion))
  1030.       return true;
  1031.  
  1032.     // Check if the toolkit version matches
  1033.     return this.matchesTargetRange(TOOLKIT_ID, toolkitVersion);
  1034.   },
  1035.  
  1036.   /**
  1037.    * Checks if a version is higher than or equal to the minVersion (if provided)
  1038.    * and lower than or equal to the maxVersion (if provided).
  1039.    * @param   version
  1040.    *          The version to test.
  1041.    * @param   minVersion
  1042.    *          The minimum version. If null it is assumed that version is always
  1043.    *          larger.
  1044.    * @param   maxVersion
  1045.    *          The maximum version. If null it is assumed that version is always
  1046.    *          smaller.
  1047.    */
  1048.   matchesRange: function(version, minVersion, maxVersion) {
  1049.     if (minVersion && gVersionChecker.compare(version, minVersion) < 0)
  1050.       return false;
  1051.     if (maxVersion && gVersionChecker.compare(version, maxVersion) > 0)
  1052.       return false;
  1053.     return true;
  1054.   },
  1055.  
  1056.   /**
  1057.    * Tests if there is a matching range for the given target application id and
  1058.    * version.
  1059.    * @param   appID
  1060.    *          The application ID to test for, may be for an application or toolkit
  1061.    * @param   appVersion
  1062.    *          The version of the application to test for.
  1063.    * @returns True if this version range covers the application version given.
  1064.    */
  1065.   matchesTargetRange: function(appID, appVersion) {
  1066.     var blTargetApp = this.targetApps[appID];
  1067.     if (!blTargetApp)
  1068.       return false;
  1069.  
  1070.     for (var x = 0; x < blTargetApp.length; ++x) {
  1071.       if (this.matchesRange(appVersion, blTargetApp[x].minVersion, blTargetApp[x].maxVersion))
  1072.         return true;
  1073.     }
  1074.  
  1075.     return false;
  1076.   },
  1077.  
  1078.   /**
  1079.    * Retrieves a version range (e.g. minVersion and maxVersion) for a
  1080.    * blocklist item's targetApplication element.
  1081.    * @param   targetAppElement
  1082.    *          A targetApplication blocklist element.
  1083.    * @returns An array of JS objects with the following properties:
  1084.    *          "minVersion"  The minimum version in a version range (default = null).
  1085.    *          "maxVersion"  The maximum version in a version range (default = null).
  1086.    */
  1087.   getBlocklistAppVersions: function(targetAppElement) {
  1088.     var appVersions = [ ];
  1089.  
  1090.     if (targetAppElement) {
  1091.       for (var i = 0; i < targetAppElement.childNodes.length; ++i) {
  1092.         var versionRangeElement = targetAppElement.childNodes.item(i);
  1093.         if (!(versionRangeElement instanceof Ci.nsIDOMElement) ||
  1094.             versionRangeElement.localName != "versionRange")
  1095.           continue;
  1096.         appVersions.push(this.getBlocklistVersionRange(versionRangeElement));
  1097.       }
  1098.     }
  1099.     // return minVersion = null and maxVersion = null if no specific versionRange
  1100.     // elements were found
  1101.     if (appVersions.length == 0)
  1102.       appVersions.push(this.getBlocklistVersionRange(null));
  1103.     return appVersions;
  1104.   },
  1105.  
  1106.   /**
  1107.    * Retrieves a version range (e.g. minVersion and maxVersion) for a blocklist
  1108.    * versionRange element.
  1109.    * @param   versionRangeElement
  1110.    *          The versionRange blocklist element.
  1111.    * @returns A JS object with the following properties:
  1112.    *          "minVersion"  The minimum version in a version range (default = null).
  1113.    *          "maxVersion"  The maximum version in a version range (default = null).
  1114.    */
  1115.   getBlocklistVersionRange: function(versionRangeElement) {
  1116.     var minVersion = null;
  1117.     var maxVersion = null;
  1118.     if (!versionRangeElement)
  1119.       return { minVersion: minVersion, maxVersion: maxVersion };
  1120.  
  1121.     if (versionRangeElement.hasAttribute("minVersion"))
  1122.       minVersion = versionRangeElement.getAttribute("minVersion");
  1123.     if (versionRangeElement.hasAttribute("maxVersion"))
  1124.       maxVersion = versionRangeElement.getAttribute("maxVersion");
  1125.  
  1126.     return { minVersion: minVersion, maxVersion: maxVersion };
  1127.   }
  1128. };
  1129.  
  1130. var NSGetFactory = XPCOMUtils.generateNSGetFactory([Blocklist]);
  1131.