home *** CD-ROM | disk | FTP | other *** search
/ PC Advisor 2010 April / PCA177.iso / ESSENTIALS / Firefox Setup.exe / nonlocalized / components / nsBlocklistService.js < prev    next >
Encoding:
Text File  |  2009-07-15  |  36.4 KB  |  1,070 lines

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