Bug 271559, Plugins blocklisting, r=robstrong
authorflamingice@sourmilk.net
Wed, 15 Aug 2007 17:43:12 -0700
changeset 4708 4eb0f0cb6ed3ab92ecdc54089197c9a85652d6eb
parent 4707 7e4964a889ee4cf11afdfd059f8d90b18c876d6e
child 4709 d0ae0ad6801559d1781f674029d1526b8a7ef5c5
push idunknown
push userunknown
push dateunknown
reviewersrobstrong
bugs271559
milestone1.9a8pre
Bug 271559, Plugins blocklisting, r=robstrong
browser/installer/unix/packages-static
browser/installer/windows/packages-static
toolkit/mozapps/extensions/public/Makefile.in
toolkit/mozapps/extensions/public/nsIBlocklistService.idl
toolkit/mozapps/extensions/src/Makefile.in
toolkit/mozapps/extensions/src/nsBlocklistService.js
toolkit/mozapps/extensions/src/nsExtensionManager.js.in
--- a/browser/installer/unix/packages-static
+++ b/browser/installer/unix/packages-static
@@ -213,16 +213,17 @@ bin/components/nsHelperAppDlg.js
 bin/components/nsInterfaceInfoToIDL.js
 ; bin/components/nsProgressDialog.js   not needed for firefox
 bin/components/nsProxyAutoConfig.js
 ; bin/components/nsResetPref.js    not needed for firefox
 bin/components/nsSidebar.js
 ; bin/components/nsUpdateNotifier.js not needed for firefox
 bin/components/nsXmlRpcClient.js
 bin/components/nsExtensionManager.js
+bin/components/nsBlocklistService.js
 bin/components/nsUpdateService.js
 bin/components/pluginGlue.js
 bin/components/extensions.xpt
 bin/components/update.xpt
 bin/components/nsSessionStartup.js
 bin/components/nsSessionStore.js
 bin/components/sessionstore.xpt
 bin/components/nsURLFormatter.js
--- a/browser/installer/windows/packages-static
+++ b/browser/installer/windows/packages-static
@@ -206,16 +206,17 @@ bin\components\nsTryToClose.js
 bin\components\nsDictionary.js
 bin\components\nsHelperAppDlg.js
 bin\components\nsProxyAutoConfig.js
 bin\components\nsSearchService.js
 bin\components\nsSearchSuggestions.js
 bin\components\nsSidebar.js
 bin\components\nsXmlRpcClient.js
 bin\components\nsExtensionManager.js
+bin\components\nsBlocklistService.js
 bin\components\nsUpdateService.js
 bin\components\nsMicrosummaryService.js
 bin\components\nsPlacesTransactionsService.js
 bin\components\nsPostUpdateWin.js
 bin\components\nsLoginInfo.js
 bin\components\nsLoginManager.js
 bin\components\nsLoginManagerPrompter.js
 bin\components\storage-Legacy.js
--- a/toolkit/mozapps/extensions/public/Makefile.in
+++ b/toolkit/mozapps/extensions/public/Makefile.in
@@ -39,12 +39,12 @@ topsrcdir = @top_srcdir@
 srcdir    = @srcdir@
 VPATH   = @srcdir@
 
 include $(DEPTH)/config/autoconf.mk
 
 MODULE        = extensions
 XPIDL_MODULE  = extensions
 
-XPIDLSRCS = nsIExtensionManager.idl
+XPIDLSRCS = nsIExtensionManager.idl nsIBlocklistService.idl
 
 include $(topsrcdir)/config/rules.mk
 
new file mode 100644
--- /dev/null
+++ b/toolkit/mozapps/extensions/public/nsIBlocklistService.idl
@@ -0,0 +1,64 @@
+/* -*- Mode: IDL; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* ***** BEGIN LICENSE BLOCK *****
+ * Version: MPL 1.1/GPL 2.0/LGPL 2.1
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is the Blocklist Service.
+ *
+ * The Initial Developer of the Original Code is
+ * Mozilla Corporation
+ * Portions created by the Initial Developer are Copyright (C) 2007
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ *   Michael Wu <flamingice@sourmilk.net>  (original author)
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * either the GNU General Public License Version 2 or later (the "GPL"), or
+ * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+ * in which case the provisions of the GPL or the LGPL are applicable instead
+ * of those above. If you wish to allow use of your version of this file only
+ * under the terms of either the GPL or the LGPL, and not to allow others to
+ * use your version of this file under the terms of the MPL, indicate your
+ * decision by deleting the provisions above and replace them with the notice
+ * and other provisions required by the GPL or the LGPL. If you do not delete
+ * the provisions above, a recipient may use your version of this file under
+ * the terms of any one of the MPL, the GPL or the LGPL.
+ *
+ * ***** END LICENSE BLOCK ***** */
+
+
+#include "nsISupports.idl"
+
+[scriptable, uuid(0c3fe697-d50d-4f42-b747-0c5855cfc60e)]
+interface nsIBlocklistService : nsISupports
+{
+  /**
+   * Determine if an item is blocklisted
+   * @param   id
+   *          The GUID of the item.
+   * @param   version
+   *          The item's version.
+   * @param   appVersion
+   *          The version of the application we are checking in the blocklist.
+   *          If this parameter is undefined, the version of the running
+   *          application is used.
+   * @param   toolkitVersion
+   *          The version of the toolkit we are checking in the blocklist.
+   *          If this parameter is undefined, the version of the running
+   *          toolkit is used.
+   * @returns true if the item is compatible with this version of the
+   *          application or this version of the toolkit, false, otherwise.
+   */
+  boolean isAddonBlocklisted(in AString id, in AString version,
+                             in AString appVersion, in AString toolkitVersion);
+};
--- a/toolkit/mozapps/extensions/src/Makefile.in
+++ b/toolkit/mozapps/extensions/src/Makefile.in
@@ -40,15 +40,16 @@ topsrcdir = @top_srcdir@
 srcdir    = @srcdir@
 VPATH   = @srcdir@
 
 include $(DEPTH)/config/autoconf.mk
 
 MODULE = extensions
 
 EXTRA_COMPONENTS = nsExtensionManager.js
+EXTRA_PP_COMPONENTS = nsBlocklistService.js
 GARBAGE += nsExtensionManager.js
 
 include $(topsrcdir)/config/rules.mk
 
 nsExtensionManager.js: nsExtensionManager.js.in
 	$(PYTHON) $(MOZILLA_DIR)/config/Preprocessor.py $(DEFINES) $(ACDEFINES) $^ > $@ 
 
new file mode 100644
--- /dev/null
+++ b/toolkit/mozapps/extensions/src/nsBlocklistService.js
@@ -0,0 +1,696 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/*
+ # ***** BEGIN LICENSE BLOCK *****
+ # Version: MPL 1.1/GPL 2.0/LGPL 2.1
+ #
+ # The contents of this file are subject to the Mozilla Public License Version
+ # 1.1 (the "License"); you may not use this file except in compliance with
+ # the License. You may obtain a copy of the License at
+ # http://www.mozilla.org/MPL/
+ #
+ # Software distributed under the License is distributed on an "AS IS" basis,
+ # WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ # for the specific language governing rights and limitations under the
+ # License.
+ #
+ # The Original Code is the Blocklist Service.
+ #
+ # The Initial Developer of the Original Code is
+ # Mozilla Corporation.
+ # Portions created by the Initial Developer are Copyright (C) 2007
+ # the Initial Developer. All Rights Reserved.
+ #
+ # Contributor(s):
+ #   Robert Strong <robert.bugzilla@gmail.com>
+ #   Michael Wu <flamingice@sourmilk.net>
+ #
+ # Alternatively, the contents of this file may be used under the terms of
+ # either the GNU General Public License Version 2 or later (the "GPL"), or
+ # the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+ # in which case the provisions of the GPL or the LGPL are applicable instead
+ # of those above. If you wish to allow use of your version of this file only
+ # under the terms of either the GPL or the LGPL, and not to allow others to
+ # use your version of this file under the terms of the MPL, indicate your
+ # decision by deleting the provisions above and replace them with the notice
+ # and other provisions required by the GPL or the LGPL. If you do not delete
+ # the provisions above, a recipient may use your version of this file under
+ # the terms of any one of the MPL, the GPL or the LGPL.
+ #
+ # ***** END LICENSE BLOCK *****
+ */
+
+const Cc = Components.classes;
+const Ci = Components.interfaces;
+const Cr = Components.results;
+
+const kELEMENT_NODE                   = Ci.nsIDOMNode.ELEMENT_NODE;
+const TOOLKIT_ID                      = "toolkit@mozilla.org"
+const KEY_PROFILEDIR                  = "ProfD";
+const FILE_BLOCKLIST                  = "blocklist.xml";
+const PREF_BLOCKLIST_URL              = "extensions.blocklist.url";
+const PREF_BLOCKLIST_ENABLED          = "extensions.blocklist.enabled";
+const PREF_BLOCKLIST_INTERVAL         = "extensions.blocklist.interval";
+const PREF_EM_LOGGING_ENABLED         = "extensions.logging.enabled";
+const XMLURI_BLOCKLIST                = "http://www.mozilla.org/2006/addons-blocklist";
+const XMLURI_PARSE_ERROR              = "http://www.mozilla.org/newlayout/xml/parsererror.xml"
+
+const MODE_RDONLY   = 0x01;
+const MODE_WRONLY   = 0x02;
+const MODE_CREATE   = 0x08;
+const MODE_APPEND   = 0x10;
+const MODE_TRUNCATE = 0x20;
+
+const PERMS_FILE      = 0644;
+const PERMS_DIRECTORY = 0755;
+
+const CID = Components.ID("{66354bc9-7ed1-4692-ae1d-8da97d6b205e}");
+const CONTRACT_ID = "@mozilla.org/extensions/blocklist;1"
+const CLASS_NAME = "Blocklist Service";
+
+var gApp = null;
+var gPref = null;
+var gOS = null;
+var gConsole = null;
+var gVersionChecker = null;
+var gLoggingEnabled = null;
+
+// shared code for suppressing bad cert dialogs
+#include ../../shared/src/badCertHandler.js
+
+/**
+ * Logs a string to the error console.
+ * @param   string
+ *          The string to write to the error console..
+ */
+function LOG(string) {
+  if (gLoggingEnabled) {
+    dump("*** " + string + "\n");
+    gConsole.logStringMessage(string);
+  }
+}
+
+/**
+ * Gets a preference value, handling the case where there is no default.
+ * @param   func
+ *          The name of the preference function to call, on nsIPrefBranch
+ * @param   preference
+ *          The name of the preference
+ * @param   defaultValue
+ *          The default value to return in the event the preference has
+ *          no setting
+ * @returns The value of the preference, or undefined if there was no
+ *          user or default value.
+ */
+function getPref(func, preference, defaultValue) {
+  try {
+    return gPref[func](preference);
+  }
+  catch (e) {
+  }
+  return defaultValue;
+}
+
+/**
+ * Gets the file at the specified hierarchy under a Directory Service key.
+ * @param   key
+ *          The Directory Service Key to start from
+ * @param   pathArray
+ *          An array of path components to locate beneath the directory
+ *          specified by |key|. The last item in this array must be the
+ *          leaf name of a file.
+ * @return  nsIFile object for the file specified. The file is NOT created
+ *          if it does not exist, however all required directories along
+ *          the way are.
+ */
+function getFile(key, pathArray) {
+  var fileLocator = Cc["@mozilla.org/file/directory_service;1"].
+                    getService(Ci.nsIProperties);
+  var file = fileLocator.get(key, Ci.nsILocalFile);
+  for (var i = 0; i < pathArray.length - 1; ++i) {
+    file.append(pathArray[i]);
+    if (!file.exists())
+      file.create(Ci.nsILocalFile.DIRECTORY_TYPE, PERMS_DIRECTORY);
+  }
+  file.followLinks = false;
+  file.append(pathArray[pathArray.length - 1]);
+  return file;
+}
+
+/**
+ * Opens a safe file output stream for writing. 
+ * @param   file
+ *          The file to write to.
+ * @param   modeFlags
+ *          (optional) File open flags. Can be undefined. 
+ * @returns nsIFileOutputStream to write to.
+ */
+function openSafeFileOutputStream(file, modeFlags) {
+  var fos = Cc["@mozilla.org/network/safe-file-output-stream;1"].
+            createInstance(Ci.nsIFileOutputStream);
+  if (modeFlags === undefined)
+    modeFlags = MODE_WRONLY | MODE_CREATE | MODE_TRUNCATE;
+  if (!file.exists()) 
+    file.create(Ci.nsILocalFile.NORMAL_FILE_TYPE, PERMS_FILE);
+  fos.init(file, modeFlags, PERMS_FILE, 0);
+  return fos;
+}
+
+/**
+ * Closes a safe file output stream.
+ * @param   stream
+ *          The stream to close.
+ */
+function closeSafeFileOutputStream(stream) {
+  if (stream instanceof Ci.nsISafeOutputStream)
+    stream.finish();
+  else
+    stream.close();
+}
+
+/**
+ * Constructs a URI to a spec.
+ * @param   spec
+ *          The spec to construct a URI to
+ * @returns The nsIURI constructed.
+ */
+function newURI(spec) {
+  var ioServ = Cc["@mozilla.org/network/io-service;1"].
+               getService(Ci.nsIIOService);
+  return ioServ.newURI(spec, null, null);
+}
+
+/**
+ * Manages the Blocklist. The Blocklist is a representation of the contents of
+ * blocklist.xml and allows us to remotely disable / re-enable blocklisted
+ * items managed by the Extension Manager with an item's appDisabled property.
+ * It also blocklists plugins with data from blocklist.xml.
+ */
+
+function Blocklist() {
+  gApp = Cc["@mozilla.org/xre/app-info;1"].getService(Ci.nsIXULAppInfo);
+  gApp.QueryInterface(Ci.nsIXULRuntime);
+  gPref = Cc["@mozilla.org/preferences-service;1"].
+          getService(Ci.nsIPrefBranch2);
+  gVersionChecker = Cc["@mozilla.org/xpcom/version-comparator;1"].
+                    getService(Ci.nsIVersionComparator);
+  gConsole = Cc["@mozilla.org/consoleservice;1"].
+             getService(Ci.nsIConsoleService);  
+  
+  gOS = Cc["@mozilla.org/observer-service;1"].
+        getService(Ci.nsIObserverService);
+  gOS.addObserver(this, "xpcom-shutdown", false);
+}
+
+Blocklist.prototype = {
+  /**
+   * Extension ID -> array of Version Ranges
+   * Each value in the version range array is a JS Object that has the
+   * following properties:
+   *   "minVersion"  The minimum version in a version range (default = 0)
+   *   "maxVersion"  The maximum version in a version range (default = *)
+   *   "targetApps"  Application ID -> array of Version Ranges
+   *                 (default = current application ID)
+   *                 Each value in the version range array is a JS Object that
+   *                 has the following properties:
+   *                   "minVersion"  The minimum version in a version range
+   *                                 (default = 0)
+   *                   "maxVersion"  The maximum version in a version range
+   *                                 (default = *)
+   */
+  _addonEntries: null,
+  _pluginEntries: null,
+
+  observe: function (aSubject, aTopic, aData) {
+    switch (aTopic) {
+    case "app-startup":
+      gOS.addObserver(this, "plugins-list-updated", false);
+      gOS.addObserver(this, "profile-after-change", false);
+      break;
+    case "profile-after-change":
+      gLoggingEnabled = getPref("getBoolPref", PREF_EM_LOGGING_ENABLED, false);
+      var tm = Cc["@mozilla.org/updates/timer-manager;1"].
+               getService(Ci.nsIUpdateTimerManager);
+      var interval = getPref("getIntPref", PREF_BLOCKLIST_INTERVAL, 86400);
+      tm.registerTimer("blocklist-background-update-timer", this, interval);
+      break;
+    case "plugins-list-updated":
+      this._checkPluginsList();
+      break;
+    case "xpcom-shutdown":
+      gOS.removeObserver(this, "xpcom-shutdown");
+      gOS.removeObserver(this, "profile-after-change");
+      gOS.removeObserver(this, "plugins-list-updated");
+      gOS = null;
+      gPref = null;
+      gConsole = null;
+      gVersionChecker = null;
+      gApp = null;
+      break;
+    }
+  },
+
+  isAddonBlocklisted: function(id, version, appVersion, toolkitVersion) {
+    if (!this._addonEntries)
+      this._loadBlocklistFromFile(getFile(KEY_PROFILEDIR, [FILE_BLOCKLIST]));
+    if (appVersion === undefined)
+      appVersion = gApp.version;
+    if (toolkitVersion === undefined)
+      toolkitVersion = gApp.platformVersion;
+
+    var blItem = this._addonEntries[id];
+    if (!blItem)
+      return false;
+
+    for (var i = 0; i < blItem.length; ++i) {
+      if (gVersionChecker.compare(version, blItem[i].minVersion) < 0  ||
+          gVersionChecker.compare(version, blItem[i].maxVersion) > 0)
+        continue;
+
+      var blTargetApp = blItem[i].targetApps[gApp.ID];
+      if (blTargetApp) {
+        for (var x = 0; x < blTargetApp.length; ++x) {
+          if (gVersionChecker.compare(appVersion, blTargetApp[x].minVersion) < 0 ||
+              gVersionChecker.compare(appVersion, blTargetApp[x].maxVersion) > 0)
+            continue;
+          return true;
+        }
+      }
+
+      blTargetApp = blItem[i].targetApps[TOOLKIT_ID];
+      if (!blTargetApp)
+        return false;
+      for (x = 0; x < blTargetApp.length; ++x) {
+        if (gVersionChecker.compare(toolkitVersion, blTargetApp[x].minVersion) < 0 ||
+            gVersionChecker.compare(toolkitVersion, blTargetApp[x].maxVersion) > 0)
+          continue;
+        return true;
+      }
+    }
+    return false;
+  },
+
+  notify: function(aTimer) {
+    if (getPref("getBoolPref", PREF_BLOCKLIST_ENABLED, true) == false)
+      return;
+
+    try {
+      var dsURI = gPref.getCharPref(PREF_BLOCKLIST_URL);
+    }
+    catch (e) {
+      LOG("Blocklist::notify: The " + PREF_BLOCKLIST_URL + " preference" + 
+          " is missing!");
+      return;
+    }
+
+    dsURI = dsURI.replace(/%APP_ID%/g, gApp.ID);
+    dsURI = dsURI.replace(/%APP_VERSION%/g, gApp.version);
+    // Verify that the URI is valid
+    try {
+      var uri = newURI(dsURI);
+    }
+    catch (e) {
+      LOG("Blocklist::notify: There was an error creating the blocklist URI\r\n" + 
+          "for: " + dsURI + ", error: " + e);
+      return;
+    }
+
+    var request = Cc["@mozilla.org/xmlextras/xmlhttprequest;1"].
+                  createInstance(Ci.nsIXMLHttpRequest);
+    request.open("GET", uri.spec, true);
+    request.channel.notificationCallbacks = new BadCertHandler();
+    request.overrideMimeType("text/xml");
+    request.setRequestHeader("Cache-Control", "no-cache");
+    request.QueryInterface(Components.interfaces.nsIJSXMLHttpRequest);
+
+    var self = this;
+    request.onerror = function(event) { self.onXMLError(event); };
+    request.onload  = function(event) { self.onXMLLoad(event);  };
+    request.send(null);
+  },
+
+  onXMLLoad: function(aEvent) {
+    var request = aEvent.target;
+    try {
+      checkCert(request.channel);
+    }
+    catch (e) {
+      LOG("Blocklist::onXMLLoad: " + e);
+      return;
+    }
+    var responseXML = request.responseXML;
+    if (!responseXML || responseXML.documentElement.namespaceURI == XMLURI_PARSE_ERROR ||
+        (request.status != 200 && request.status != 0)) {
+      LOG("Blocklist::onXMLLoad: there was an error during load");
+      return;
+    }
+    var blocklistFile = getFile(KEY_PROFILEDIR, [FILE_BLOCKLIST]);
+    if (blocklistFile.exists())
+      blocklistFile.remove(false);
+    var fos = openSafeFileOutputStream(blocklistFile);
+    fos.write(request.responseText, request.responseText.length);
+    closeSafeFileOutputStream(fos);
+    this._loadBlocklistFromFile(getFile(KEY_PROFILEDIR, [FILE_BLOCKLIST]));
+    var em = Cc["@mozilla.org/extensions/manager;1"].
+             getService(Ci.nsIExtensionManager);
+    em.checkForBlocklistChanges();
+    this._checkPluginsList();
+  },
+
+  onXMLError: function(aEvent) {
+    try {
+      var request = aEvent.target;
+      // the following may throw (e.g. a local file or timeout)
+      var status = request.status;
+    }
+    catch (e) {
+      request = aEvent.target.channel.QueryInterface(Ci.nsIRequest);
+      status = request.status;
+    }
+    var statusText = request.statusText;
+    // When status is 0 we don't have a valid channel.
+    if (status == 0)
+      statusText = "nsIXMLHttpRequest channel unavailable";
+    LOG("Blocklist:onError: There was an error loading the blocklist file\r\n" +
+        statusText);
+  },
+
+  /**
+   # The blocklist XML file looks something like this:
+   #
+   # <blocklist xmlns="http://www.mozilla.org/2006/addons-blocklist">
+   #   <emItems>
+   #     <emItem id="item_1@domain">
+   #       <versionRange minVersion="1.0" maxVersion="2.0.*">
+   #         <targetApplication id="{ec8030f7-c20a-464f-9b0e-13a3a9e97384}">
+   #           <versionRange minVersion="1.5" maxVersion="1.5.*"/>
+   #           <versionRange minVersion="1.7" maxVersion="1.7.*"/>
+   #         </targetApplication>
+   #         <targetApplication id="toolkit@mozilla.org">
+   #           <versionRange minVersion="1.8" maxVersion="1.8.*"/>
+   #         </targetApplication>
+   #       </versionRange>
+   #       <versionRange minVersion="3.0" maxVersion="3.0.*">
+   #         <targetApplication id="{ec8030f7-c20a-464f-9b0e-13a3a9e97384}">
+   #           <versionRange minVersion="1.5" maxVersion="1.5.*"/>
+   #         </targetApplication>
+   #         <targetApplication id="toolkit@mozilla.org">
+   #           <versionRange minVersion="1.8" maxVersion="1.8.*"/>
+   #         </targetApplication>
+   #       </versionRange>
+   #     </emItem>
+   #     <emItem id="item_2@domain">
+   #       <versionRange minVersion="3.1" maxVersion="4.*"/>
+   #     </emItem>
+   #     <emItem id="item_3@domain">
+   #       <versionRange>
+   #         <targetApplication id="{ec8030f7-c20a-464f-9b0e-13a3a9e97384}">
+   #           <versionRange minVersion="1.5" maxVersion="1.5.*"/>
+   #         </targetApplication>
+   #       </versionRange>
+   #     </emItem>
+   #     <emItem id="item_4@domain">
+   #       <versionRange>
+   #         <targetApplication>
+   #           <versionRange minVersion="1.5" maxVersion="1.5.*"/>
+   #         </targetApplication>
+   #       </versionRange>
+   #     <emItem id="item_5@domain"/>
+   #   </emItems>
+   #   <pluginItems>
+   #     <pluginItem>
+   #       <!-- All match tags must match a plugin to blocklist a plugin -->
+   #       <match name="name" exp="some plugin"/>
+   #       <match name="description" exp="1[.]2[.]3"/>
+   #     </pluginItem>
+   #   </pluginItems>
+   # </blocklist> 
+   */
+
+  _loadBlocklistFromFile: function(file) {
+    this._addonEntries = { };
+    this._pluginEntries = { };
+    if (getPref("getBoolPref", PREF_BLOCKLIST_ENABLED, true) == false) {
+      LOG("Blocklist::_loadBlocklistFromFile: blocklist is disabled");
+      return;
+    }
+
+    if (!file.exists()) {
+      LOG("Blocklist::_loadBlocklistFromFile: XML File does not exist");
+      return;
+    }
+
+    var fileStream = Components.classes["@mozilla.org/network/file-input-stream;1"]
+                               .createInstance(Components.interfaces.nsIFileInputStream);
+    fileStream.init(file, MODE_RDONLY, PERMS_FILE, 0);
+    try {
+      var parser = Cc["@mozilla.org/xmlextras/domparser;1"].
+                   createInstance(Ci.nsIDOMParser);
+      var doc = parser.parseFromStream(fileStream, "UTF-8", file.fileSize, "text/xml");
+      if (doc.documentElement.namespaceURI != XMLURI_BLOCKLIST) {
+        LOG("Blocklist::_loadBlocklistFromFile: aborting due to incorrect " +
+            "XML Namespace.\r\nExpected: " + XMLURI_BLOCKLIST + "\r\n" +
+            "Received: " + doc.documentElement.namespaceURI);
+        return;
+      }
+
+      var childNodes = doc.documentElement.childNodes;
+      this._addonEntries = this._processItemNodes(childNodes, "em",
+                                            this._handleEmItemNode);
+      this._pluginEntries = this._processItemNodes(childNodes, "plugin",
+                                                   this._handlePluginItemNode);
+    }
+    catch (e) {
+      LOG("Blocklist::_loadBlocklistFromFile: Error constructing blocklist " + e);
+      return;
+    }
+    fileStream.close();
+  },
+
+  _processItemNodes: function(deChildNodes, prefix, handler) {
+    var result = [];
+    var itemNodes;
+    var containerName = prefix + "Items";
+    for (var i = 0; i < deChildNodes.length; ++i) {
+      var emItemsElement = deChildNodes[i];
+      if (emItemsElement.nodeType == kELEMENT_NODE &&
+          emItemsElement.localName == containerName) {
+        itemNodes = emItemsElement.childNodes;
+        break;
+      }
+    }
+    if (!itemNodes)
+      return result;
+
+    var itemName = prefix + "Item";
+    for (var i = 0; i < itemNodes.length; ++i) {
+      var blocklistElement = itemNodes[i];
+      if (blocklistElement.nodeType != kELEMENT_NODE ||
+          blocklistElement.localName != itemName)
+        continue;
+
+      blocklistElement.QueryInterface(Ci.nsIDOMElement);
+      handler(blocklistElement, result);
+    }
+    return result;
+  },
+
+  _handleEmItemNode: function(blocklistElement, result) {
+    var versionNodes = blocklistElement.childNodes;
+    var id = blocklistElement.getAttribute("id");
+    result[id] = [];
+    for (var x = 0; x < versionNodes.length; ++x) {
+      var versionRangeElement = versionNodes[x];
+      if (versionRangeElement.nodeType != kELEMENT_NODE ||
+          versionRangeElement.localName != "versionRange")
+        continue;
+
+      result[id].push(new BlocklistItemData(versionRangeElement));
+    }
+    // if only the extension ID is specified block all versions of the
+    // extension for the current application.
+    if (result[id].length == 0)
+      result[id].push(new BlocklistItemData(null));
+  },
+
+  _handlePluginItemNode: function(blocklistElement, result) {
+    var matchNodes = blocklistElement.childNodes;
+    var matchList;
+    for (var x = 0; x < matchNodes.length; ++x) {
+      var matchElement = matchNodes[x];
+      if (matchElement.nodeType != kELEMENT_NODE ||
+          matchElement.localName != "match")
+        continue;
+
+      var name = matchElement.getAttribute("name");
+      var exp = matchElement.getAttribute("exp");
+      if (!matchList)
+        matchList = { };
+      matchList[name] = new RegExp(exp, "m");
+    }
+    if (matchList)
+      result.push(matchList);
+  },
+
+  _checkPlugin: function(plugin) {
+    for each (var matchList in this._pluginEntries) {
+      var matchFailed = false;
+      for (var name in matchList) {
+        if (typeof(plugin[name]) != "string" ||
+            !matchList[name].test(plugin[name])) {
+          matchFailed = true;
+          break;
+        }
+      }
+
+      if (!matchFailed) {
+        plugin.blocklisted = true;
+        return;
+      }
+    }
+    plugin.blocklisted = false;
+  },
+
+  _checkPluginsList: function() {
+    if (!this._addonEntries)
+      this._loadBlocklistFromFile(getFile(KEY_PROFILEDIR, [FILE_BLOCKLIST]));
+    var phs = Cc["@mozilla.org/plugin/host;1"].
+              getService(Ci.nsIPluginHost);
+    phs.getPluginTags({ }).forEach(this._checkPlugin, this);
+  },
+
+  QueryInterface: function(aIID) {
+    if (!aIID.equals(Ci.nsIObserver) &&
+        !aIID.equals(Ci.nsIBlocklistService) &&
+        !aIID.equals(Ci.nsITimerCallback) &&
+        !aIID.equals(Ci.nsISupports))
+      throw Cr.NS_ERROR_NO_INTERFACE;
+    return this;
+  }
+};
+
+/**
+ * Helper for constructing a blocklist.
+ */
+function BlocklistItemData(versionRangeElement) {
+  var versionRange = this.getBlocklistVersionRange(versionRangeElement);
+  this.minVersion = versionRange.minVersion;
+  this.maxVersion = versionRange.maxVersion;
+  this.targetApps = { };
+  var found = false;
+
+  if (versionRangeElement) {
+    for (var i = 0; i < versionRangeElement.childNodes.length; ++i) {
+      var targetAppElement = versionRangeElement.childNodes[i];
+      if (targetAppElement.nodeType != Ci.nsIDOMNode.ELEMENT_NODE ||
+          targetAppElement.localName != "targetApplication")
+        continue;
+      found = true;
+      // default to the current application if id is not provided.
+      var appID = targetAppElement.hasAttribute("id") ? targetAppElement.getAttribute("id") : gApp.ID;
+      this.targetApps[appID] = this.getBlocklistAppVersions(targetAppElement);
+    }
+  }
+  // Default to all versions of the extension and the current application when
+  // versionRange is not defined.
+  if (!found)
+    this.targetApps[gApp.ID] = this.getBlocklistAppVersions(null);
+}
+
+BlocklistItemData.prototype = {
+/**
+ * Retrieves a version range (e.g. minVersion and maxVersion) for a
+ * blocklist item's targetApplication element.
+ * @param   targetAppElement
+ *          A targetApplication blocklist element.
+ * @returns An array of JS objects with the following properties:
+ *          "minVersion"  The minimum version in a version range (default = 0).
+ *          "maxVersion"  The maximum version in a version range (default = *).
+ */
+  getBlocklistAppVersions: function(targetAppElement) {
+    var appVersions = [ ];
+    var found = false;
+
+    if (targetAppElement) {
+      for (var i = 0; i < targetAppElement.childNodes.length; ++i) {
+        var versionRangeElement = targetAppElement.childNodes[i];
+        if (versionRangeElement.nodeType != Ci.nsIDOMNode.ELEMENT_NODE ||
+            versionRangeElement.localName != "versionRange")
+          continue;
+        found = true;
+        appVersions.push(this.getBlocklistVersionRange(versionRangeElement));
+      }
+    }
+    // return minVersion = 0 and maxVersion = * if not available
+    if (!found)
+      return [ this.getBlocklistVersionRange(null) ];
+    return appVersions;
+  },
+
+/**
+ * Retrieves a version range (e.g. minVersion and maxVersion) for a blocklist
+ * versionRange element.
+ * @param   versionRangeElement
+ *          The versionRange blocklist element.
+ * @returns A JS object with the following properties:
+ *          "minVersion"  The minimum version in a version range (default = 0).
+ *          "maxVersion"  The maximum version in a version range (default = *).
+ */
+  getBlocklistVersionRange: function(versionRangeElement) {
+    var minVersion = "0";
+    var maxVersion = "*";
+    if (!versionRangeElement)
+      return { minVersion: minVersion, maxVersion: maxVersion };
+
+    if (versionRangeElement.hasAttribute("minVersion"))
+      minVersion = versionRangeElement.getAttribute("minVersion");
+    if (versionRangeElement.hasAttribute("maxVersion"))
+      maxVersion = versionRangeElement.getAttribute("maxVersion");
+
+    return { minVersion: minVersion, maxVersion: maxVersion };
+  }
+};
+
+const BlocklistFactory = {
+  createInstance: function(aOuter, aIID) {
+    if (aOuter != null)
+      throw Cr.NS_ERROR_NO_AGGREGATION;
+
+    return (new Blocklist()).QueryInterface(aIID);
+  }
+};
+
+const gModule = {
+  registerSelf: function(aCompMgr, aFileSpec, aLocation, aType) {
+    aCompMgr.QueryInterface(Ci.nsIComponentRegistrar);
+    aCompMgr.registerFactoryLocation(CID, CLASS_NAME, CONTRACT_ID,
+                                     aFileSpec, aLocation, aType);
+
+    var catMan = Cc["@mozilla.org/categorymanager;1"].
+                 getService(Ci.nsICategoryManager);
+    catMan.addCategoryEntry("app-startup", CLASS_NAME, "service," + CONTRACT_ID, true, true);
+  },
+
+  unregisterSelf: function(aCompMgr, aLocation, aType) {
+    aCompMgr.QueryInterface(Ci.nsIComponentRegistrar);
+    aCompMgr.unregisterFactoryLocation(CID, aLocation);
+
+    var catMan = Cc["@mozilla.org/categorymanager;1"].
+                 getService(Ci.nsICategoryManager);
+    catMan.deleteCategoryEntry("app-startup", "service," + CONTRACT_ID, true);
+  },
+
+  getClassObject: function(aCompMgr, aCID, aIID) {
+    if (aCID.equals(CID))
+      return BlocklistFactory;
+
+    throw Cr.NS_ERROR_NOT_REGISTERED;
+  },
+
+  canUnload: function(aCompMgr) {
+    return true;
+  }
+};
+
+function NSGetModule(aCompMgr, aFileSpec) {
+  return gModule;
+}
--- a/toolkit/mozapps/extensions/src/nsExtensionManager.js.in
+++ b/toolkit/mozapps/extensions/src/nsExtensionManager.js.in
@@ -68,35 +68,30 @@ const PREF_EM_ITEM_UPDATE_ENABLED     = 
 const PREF_EM_UPDATE_ENABLED          = "extensions.update.enabled";
 const PREF_EM_ITEM_UPDATE_URL         = "extensions.%UUID%.update.url";
 const PREF_EM_DSS_ENABLED             = "extensions.dss.enabled";
 const PREF_DSS_SWITCHPENDING          = "extensions.dss.switchPending";
 const PREF_DSS_SKIN_TO_SELECT         = "extensions.lastSelectedSkin";
 const PREF_GENERAL_SKINS_SELECTEDSKIN = "general.skins.selectedSkin";
 const PREF_EM_LOGGING_ENABLED         = "extensions.logging.enabled";
 const PREF_EM_UPDATE_INTERVAL         = "extensions.update.interval";
-const PREF_BLOCKLIST_URL              = "extensions.blocklist.url";
-const PREF_BLOCKLIST_DETAILS_URL      = "extensions.blocklist.detailsURL";
-const PREF_BLOCKLIST_ENABLED          = "extensions.blocklist.enabled";
-const PREF_BLOCKLIST_INTERVAL         = "extensions.blocklist.interval";
 const PREF_UPDATE_NOTIFYUSER          = "extensions.update.notifyUser";
 const PREF_MATCH_OS_LOCALE            = "intl.locale.matchOS";
 const PREF_SELECTED_LOCALE            = "general.useragent.locale";
 
 const DIR_EXTENSIONS                  = "extensions";
 const DIR_CHROME                      = "chrome";
 const DIR_STAGE                       = "staged-xpis";
 const FILE_EXTENSIONS                 = "extensions.rdf";
 const FILE_EXTENSION_MANIFEST         = "extensions.ini";
 const FILE_EXTENSIONS_STARTUP_CACHE   = "extensions.cache";
 const FILE_AUTOREG                    = ".autoreg";
 const FILE_INSTALL_MANIFEST           = "install.rdf";
 const FILE_CONTENTS_MANIFEST          = "contents.rdf";
 const FILE_CHROME_MANIFEST            = "chrome.manifest";
-const FILE_BLOCKLIST                  = "blocklist.xml";
 
 const UNKNOWN_XPCOM_ABI               = "unknownABI";
 
 const FILE_LOGFILE                    = "extensionmanager.log";
 
 const FILE_DEFAULT_THEME_JAR          = "classic.jar";
 const TOOLKIT_ID                      = "toolkit@mozilla.org"
 
@@ -130,17 +125,16 @@ const PREFIX_NS_EM                    = 
 const PREFIX_NS_CHROME                = "http://www.mozilla.org/rdf/chrome#";
 const PREFIX_ITEM_URI                 = "urn:mozilla:item:";
 const PREFIX_EXTENSION                = "urn:mozilla:extension:";
 const PREFIX_THEME                    = "urn:mozilla:theme:";
 const RDFURI_INSTALL_MANIFEST_ROOT    = "urn:mozilla:install-manifest";
 const RDFURI_ITEM_ROOT                = "urn:mozilla:item:root"
 const RDFURI_DEFAULT_THEME            = "urn:mozilla:item:{972ce4c6-7e08-4474-a285-3208198ce6fd}";
 const XMLURI_PARSE_ERROR              = "http://www.mozilla.org/newlayout/xml/parsererror.xml"
-const XMLURI_BLOCKLIST                = "http://www.mozilla.org/2006/addons-blocklist";
 
 const URI_GENERIC_ICON_XPINSTALL      = "chrome://mozapps/skin/xpinstall/xpinstallItemGeneric.png";
 const URI_GENERIC_ICON_THEME          = "chrome://mozapps/skin/extensions/themeGeneric.png";
 const URI_XPINSTALL_CONFIRM_DIALOG    = "chrome://mozapps/content/xpinstall/xpinstallConfirm.xul";
 const URI_FINALIZE_DIALOG             = "chrome://mozapps/content/extensions/finalize.xul";
 const URI_EXTENSIONS_PROPERTIES       = "chrome://mozapps/locale/extensions/extensions.properties";
 const URI_BRAND_PROPERTIES            = "chrome://branding/locale/brand.properties";
 const URI_DOWNLOADS_PROPERTIES        = "chrome://mozapps/locale/downloads/downloads.properties";
@@ -163,16 +157,17 @@ const MODE_TRUNCATE = 0x20;
 
 const PERMS_FILE      = 0644;
 const PERMS_DIRECTORY = 0755;
 
 var gApp  = null;
 var gPref = null;
 var gRDF  = null;
 var gOS   = null;
+var gBlocklist            = null;
 var gXPCOMABI             = null;
 var gOSTarget             = null;
 var gConsole              = null;
 var gInstallManifestRoot  = null;
 var gVersionChecker       = null;
 var gLoggingEnabled       = null;
 var gCheckCompatibility   = true;
 var gLocale               = "en-US";
@@ -2299,328 +2294,16 @@ var StartupCache = {
         }
       }
     }
     closeSafeFileOutputStream(fos);
   }
 };
 
 /**
- * Manages the Blocklist. The Blocklist is a representation of the contents of
- * blocklist.xml and allows us to remotely disable / re-enable blocklisted
- * items managed by the Extension Manager with an item's appDisabled property.
- */
-var Blocklist = {
-  /**
-   * Extension ID -> array of Version Ranges
-   * Each value in the version range array is a JS Object that has the
-   * following properties:
-   *   "minVersion"  The minimum version in a version range (default = 0)
-   *   "maxVersion"  The maximum version in a version range (default = *)
-   *   "targetApps"  Application ID -> array of Version Ranges
-   *                 (default = current application ID)
-   *                 Each value in the version range array is a JS Object that
-   *                 has the following properties:
-   *                   "minVersion"  The minimum version in a version range
-   *                                 (default = 0)
-   *                   "maxVersion"  The maximum version in a version range
-   *                                 (default = *)
-   */
-  entries: null,
-
-  notify: function() {
-    if (getPref("getBoolPref", PREF_BLOCKLIST_ENABLED, true) == false)
-      return;
-
-    try {
-      var dsURI = gPref.getCharPref(PREF_BLOCKLIST_URL);
-    }
-    catch (e) {
-      LOG("Blocklist::notify: The " + PREF_BLOCKLIST_URL + " preference" + 
-          " is missing!");
-      return;
-    }
-
-    dsURI = dsURI.replace(/%APP_ID%/g, gApp.ID);
-    dsURI = dsURI.replace(/%APP_VERSION%/g, gApp.version);
-    // Verify that the URI is valid
-    try {
-      var uri = newURI(dsURI);
-    }
-    catch (e) {
-      LOG("Blocklist::notify: There was an error creating the blocklist URI\r\n" + 
-          "for: " + dsURI + ", error: " + e);
-      return;
-    }
-
-    var request = Components.classes["@mozilla.org/xmlextras/xmlhttprequest;1"]
-                            .createInstance(Components.interfaces.nsIXMLHttpRequest);
-    request.open("GET", uri.spec, true);
-    request.channel.notificationCallbacks = new BadCertHandler();
-    request.overrideMimeType("text/xml");
-    request.setRequestHeader("Cache-Control", "no-cache");
-    request.QueryInterface(Components.interfaces.nsIJSXMLHttpRequest);
-
-    var self = this;
-    request.onerror = function(event) { self.onXMLError(event); };
-    request.onload  = function(event) { self.onXMLLoad(event);  };
-    request.send(null);
-  },
-
-  onXMLLoad: function(aEvent) {
-    var request = aEvent.target;
-    try {
-      checkCert(request.channel);
-    }
-    catch (e) {
-      LOG("Blocklist::onXMLLoad: " + e);
-      return;
-    }
-    var responseXML = request.responseXML;
-    if (!responseXML || responseXML.documentElement.namespaceURI == XMLURI_PARSE_ERROR ||
-        (request.status != 200 && request.status != 0)) {
-      LOG("Blocklist::onXMLLoad: there was an error during load");
-      return;
-    }
-    var blocklistFile = getFile(KEY_PROFILEDIR, [FILE_BLOCKLIST]);
-    if (blocklistFile.exists())
-      blocklistFile.remove(false);
-    var fos = openSafeFileOutputStream(blocklistFile);
-    fos.write(request.responseText, request.responseText.length);
-    closeSafeFileOutputStream(fos);
-    this.entries = this._loadBlocklistFromFile(getFile(KEY_PROFILEDIR,
-                                                       [FILE_BLOCKLIST]));
-    var em = Components.classes["@mozilla.org/extensions/manager;1"]
-                       .getService(Components.interfaces.nsIExtensionManager);
-    em.checkForBlocklistChanges();
-  },
-
-  onXMLError: function(aEvent) {
-    try {
-      var request = aEvent.target;
-      // the following may throw (e.g. a local file or timeout)
-      var status = request.status;
-    }
-    catch (e) {
-      request = aEvent.target.channel.QueryInterface(Components.interfaces.nsIRequest);
-      status = request.status;
-    }
-    var statusText = request.statusText;
-    // When status is 0 we don't have a valid channel.
-    if (status == 0)
-      statusText = "nsIXMLHttpRequest channel unavailable";
-    LOG("Blocklist:onError: There was an error loading the blocklist file\r\n" + 
-        statusText);
-  },
-
-  /**
-   * The blocklist XML file looks something like this:
-   *
-   * <blocklist xmlns="http://www.mozilla.org/2006/addons-blocklist">
-   *   <emItems>
-   *     <emItem id="item_1@domain">
-   *       <versionRange minVersion="1.0" maxVersion="2.0.*">
-   *         <targetApplication id="{ec8030f7-c20a-464f-9b0e-13a3a9e97384}">
-   *           <versionRange minVersion="1.5" maxVersion="1.5.*"/>
-   *           <versionRange minVersion="1.7" maxVersion="1.7.*"/>
-   *         </targetApplication>
-   *         <targetApplication id="toolkit@mozilla.org">
-   *           <versionRange minVersion="1.8" maxVersion="1.8.*"/>
-   *         </targetApplication>
-   *       </versionRange>
-   *       <versionRange minVersion="3.0" maxVersion="3.0.*">
-   *         <targetApplication id="{ec8030f7-c20a-464f-9b0e-13a3a9e97384}">
-   *           <versionRange minVersion="1.5" maxVersion="1.5.*"/>
-   *         </targetApplication>
-   *         <targetApplication id="toolkit@mozilla.org">
-   *           <versionRange minVersion="1.8" maxVersion="1.8.*"/>
-   *         </targetApplication>
-   *       </versionRange>
-   *     </emItem>
-   *     <emItem id="item_2@domain">
-   *       <versionRange minVersion="3.1" maxVersion="4.*"/>
-   *     </emItem>
-   *     <emItem id="item_3@domain">
-   *       <versionRange>
-   *         <targetApplication id="{ec8030f7-c20a-464f-9b0e-13a3a9e97384}">
-   *           <versionRange minVersion="1.5" maxVersion="1.5.*"/>
-   *         </targetApplication>
-   *       </versionRange>
-   *     </emItem>
-   *     <emItem id="item_4@domain">
-   *       <versionRange>
-   *         <targetApplication>
-   *           <versionRange minVersion="1.5" maxVersion="1.5.*"/>
-   *         </targetApplication>
-   *       </versionRange>
-   *     <emItem id="item_5@domain"/>
-   *   </emItems>
-   * </blocklist> 
-   */
-  _loadBlocklistFromFile: function(file) {
-    if (getPref("getBoolPref", PREF_BLOCKLIST_ENABLED, true) == false) {
-      LOG("Blocklist::_loadBlocklistFromFile: blocklist is disabled");
-      return { };
-    }
-
-    if (!file.exists()) {
-      LOG("Blocklist::_loadBlocklistFromFile: XML File does not exist");
-      return { };
-    }
-
-    var result = { };
-    var fileStream = Components.classes["@mozilla.org/network/file-input-stream;1"]
-                               .createInstance(Components.interfaces.nsIFileInputStream);
-    fileStream.init(file, MODE_RDONLY, PERMS_FILE, 0);
-    try {
-      var parser = Components.classes["@mozilla.org/xmlextras/domparser;1"]
-                             .createInstance(Components.interfaces.nsIDOMParser);
-      var doc = parser.parseFromStream(fileStream, "UTF-8", file.fileSize, "text/xml");
-      if (doc.documentElement.namespaceURI != XMLURI_BLOCKLIST) {
-        LOG("Blocklist::_loadBlocklistFromFile: aborting due to incorrect " +
-            "XML Namespace.\r\nExpected: " + XMLURI_BLOCKLIST + "\r\n" +
-            "Received: " + doc.documentElement.namespaceURI);
-        return { };
-      }
-
-      const kELEMENT_NODE = Components.interfaces.nsIDOMNode.ELEMENT_NODE;
-      var itemNodes = this._getItemNodes(doc.documentElement.childNodes);
-      for (var i = 0; i < itemNodes.length; ++i) {
-        var blocklistElement = itemNodes[i];
-        if (blocklistElement.nodeType != kELEMENT_NODE ||
-            blocklistElement.localName != "emItem")
-          continue;
-
-        blocklistElement.QueryInterface(Components.interfaces.nsIDOMElement);
-        var versionNodes = blocklistElement.childNodes;
-        var id = blocklistElement.getAttribute("id");
-        result[id] = [];
-        for (var x = 0; x < versionNodes.length; ++x) {
-          var versionRangeElement = versionNodes[x];
-          if (versionRangeElement.nodeType != kELEMENT_NODE ||
-              versionRangeElement.localName != "versionRange")
-            continue;
-
-          result[id].push(new BlocklistItemData(versionRangeElement));
-        }
-        // if only the extension ID is specified block all versions of the
-        // extension for the current application.
-        if (result[id].length == 0)
-          result[id].push(new BlocklistItemData(null));
-      }
-    }
-    catch (e) {
-      LOG("Blocklist::_loadBlocklistFromFile: Error constructing blocklist " + e);
-      return { };
-    }
-    fileStream.close();
-    return result;
-  },
-
-  _getItemNodes: function(deChildNodes) {
-    const kELEMENT_NODE = Components.interfaces.nsIDOMNode.ELEMENT_NODE;
-    for (var i = 0; i < deChildNodes.length; ++i) {
-      var emItemsElement = deChildNodes[i];
-      if (emItemsElement.nodeType == kELEMENT_NODE &&
-          emItemsElement.localName == "emItems")
-        return emItemsElement.childNodes;
-    }
-    return [ ];
-  },
-
-  _ensureBlocklist: function() {
-    if (!this.entries)
-      this.entries = this._loadBlocklistFromFile(getFile(KEY_PROFILEDIR, 
-                                                         [FILE_BLOCKLIST]));
-  }
-};
-
-/**
- * Helper for constructing a blocklist.
- */
-function BlocklistItemData(versionRangeElement) {
-  var versionRange = this.getBlocklistVersionRange(versionRangeElement);
-  this.minVersion = versionRange.minVersion;
-  this.maxVersion = versionRange.maxVersion;
-  this.targetApps = { };
-  var found = false;
-
-  if (versionRangeElement) {
-    for (var i = 0; i < versionRangeElement.childNodes.length; ++i) {
-      var targetAppElement = versionRangeElement.childNodes[i];
-      if (targetAppElement.nodeType != Components.interfaces.nsIDOMNode.ELEMENT_NODE ||
-          targetAppElement.localName != "targetApplication")
-        continue;
-      found = true;
-      // default to the current application if id is not provided.
-      var appID = targetAppElement.hasAttribute("id") ? targetAppElement.getAttribute("id") : gApp.ID;
-      this.targetApps[appID] = this.getBlocklistAppVersions(targetAppElement);
-    }
-  }
-  // Default to all versions of the extension and the current application when
-  // versionRange is not defined.
-  if (!found)
-    this.targetApps[gApp.ID] = this.getBlocklistAppVersions(null);
-}
-
-BlocklistItemData.prototype = {
-/**
- * Retrieves a version range (e.g. minVersion and maxVersion) for a
- * blocklist item's targetApplication element.
- * @param   targetAppElement
- *          A targetApplication blocklist element.
- * @returns An array of JS objects with the following properties:
- *          "minVersion"  The minimum version in a version range (default = 0).
- *          "maxVersion"  The maximum version in a version range (default = *).
- */
-  getBlocklistAppVersions: function(targetAppElement) {
-    var appVersions = [ ];
-    var found = false;
-
-    if (targetAppElement) {
-      for (var i = 0; i < targetAppElement.childNodes.length; ++i) {
-        var versionRangeElement = targetAppElement.childNodes[i];
-        if (versionRangeElement.nodeType != Components.interfaces.nsIDOMNode.ELEMENT_NODE ||
-            versionRangeElement.localName != "versionRange")
-          continue;
-        found = true;
-        appVersions.push(this.getBlocklistVersionRange(versionRangeElement));
-      }
-    }
-    // return minVersion = 0 and maxVersion = * if not available
-    if (!found)
-      return [ this.getBlocklistVersionRange(null) ];
-    return appVersions;
-  },
-
-/**
- * Retrieves a version range (e.g. minVersion and maxVersion) for a blocklist
- * versionRange element.
- * @param   versionRangeElement
- *          The versionRange blocklist element.
- * @returns A JS object with the following properties:
- *          "minVersion"  The minimum version in a version range (default = 0).
- *          "maxVersion"  The maximum version in a version range (default = *).
- */
-  getBlocklistVersionRange: function(versionRangeElement) {
-    var minVersion = "0";
-    var maxVersion = "*";
-    if (!versionRangeElement)
-      return { minVersion: minVersion, maxVersion: maxVersion };
-
-    if (versionRangeElement.hasAttribute("minVersion"))
-      minVersion = versionRangeElement.getAttribute("minVersion");
-    if (versionRangeElement.hasAttribute("maxVersion"))
-      maxVersion = versionRangeElement.getAttribute("maxVersion");
-
-    return { minVersion: minVersion, maxVersion: maxVersion };
-  }
-};
-
-/**
  * Installs, manages and tracks compatibility for Extensions and Themes
  * @constructor
  */
 function ExtensionManager() {
   gApp = Components.classes["@mozilla.org/xre/app-info;1"]
                    .getService(Components.interfaces.nsIXULAppInfo)
                    .QueryInterface(Components.interfaces.nsIXULRuntime);
   gOSTarget = gApp.OS;
@@ -2927,19 +2610,16 @@ ExtensionManager.prototype = {
    */
   _startTimers: function() {
     // Register a background update check timer
     var tm = 
         Components.classes["@mozilla.org/updates/timer-manager;1"]
                   .getService(Components.interfaces.nsIUpdateTimerManager);
     var interval = getPref("getIntPref", PREF_EM_UPDATE_INTERVAL, 86400); 
     tm.registerTimer("addon-background-update-timer", this, interval);
-
-    interval = getPref("getIntPref", PREF_BLOCKLIST_INTERVAL, 86400); 
-    tm.registerTimer("blocklist-background-update-timer", Blocklist, interval);
   },
   
   /**
    * Notified when a timer fires
    * @param   timer
    *          The timer that fired
    */
   notify: function(timer) {
@@ -4236,17 +3916,20 @@ ExtensionManager.prototype = {
     }
      
     // Check the target application range specified by the extension metadata.
     if (gCheckCompatibility &&
         !this.datasource.isCompatible(installManifest, gInstallManifestRoot, undefined))
       installData.error = INSTALLERROR_INCOMPATIBLE_VERSION;
     
     // Check if the item is blocklisted.
-    if (this.datasource.isBlocklisted(installData.id, installData.version,
+    if (!gBlocklist)
+      gBlocklist = Components.classes["@mozilla.org/extensions/blocklist;1"]
+                             .getService(Components.interfaces.nsIBlocklistService);
+    if (gBlocklist.isAddonBlocklisted(installData.id, installData.version,
                                       undefined, undefined))
       installData.error = INSTALLERROR_BLOCKLISTED;
 
     return installData;
   },  
   
   /**
    * Installs an item from a XPI/JAR file. 
@@ -6066,18 +5749,21 @@ ExtensionItemUpdater.prototype = {
         gVersionChecker.compare(appExtensionsVersion, aMinAppVersion) < 0) 
       return false;
 
     // Check if the update will only run on an older version of Firefox. 
     if (aMaxAppVersion && 
         gVersionChecker.compare(appExtensionsVersion, aMaxAppVersion) > 0) 
       return false;
 
-    if (this._emDS.isBlocklisted(aLocalItem.id, aVersion,
-                                 undefined, undefined))
+    if (!gBlocklist)
+      gBlocklist = Components.classes["@mozilla.org/extensions/blocklist;1"]
+                             .getService(Components.interfaces.nsIBlocklistService);
+    if (gBlocklist.isAddonBlocklisted(aLocalItem.id, aVersion,
+                                      undefined, undefined))
       return false;
     
     return true;
   },
   
   checkForDone: function(item, status) {
     if (this._background &&
         status == nsIAddonUpdateCheckListener.STATUS_UPDATE) {
@@ -6668,72 +6354,16 @@ ExtensionsDataSource.prototype = {
         return ((versionChecker.compare(version, minVersion) >= 0) &&
                 (versionChecker.compare(version, maxVersion) <= 0));
       }
     }
     return false;
   },
 
   /**
-   * Determine if an item is blocklisted
-   * @param   id
-   *          The id of the item to check.
-   * @param   extVersion
-   *          The item's version.
-   * @param   appVersion
-   *          The version of the application we are checking in the blocklist.
-   *          If this parameter is undefined, the version of the running
-   *          application is used.
-   * @param   toolkitVersion
-   *          The version of the toolkit we are checking in the blocklist.
-   *          If this parameter is undefined, the version of the running
-   *          toolkit is used.
-   * @returns true if the item is compatible with this version of the 
-   *          application, false, otherwise.
-   */
-  isBlocklisted: function(id, extVersion, appVersion, toolkitVersion) {
-    if (appVersion === undefined)
-      appVersion = gApp.version;
-    if (toolkitVersion === undefined)
-      toolkitVersion = gApp.platformVersion;
-
-    var blItem = Blocklist.entries[id];
-    if (!blItem)
-      return false;
-
-    var versionChecker = getVersionChecker();
-    for (var i = 0; i < blItem.length; ++i) {
-      if (versionChecker.compare(extVersion, blItem[i].minVersion) < 0  ||
-          versionChecker.compare(extVersion, blItem[i].maxVersion) > 0)
-        continue;
-
-      var blTargetApp = blItem[i].targetApps[gApp.ID];
-      if (blTargetApp) {
-        for (var x = 0; x < blTargetApp.length; ++x) {
-          if (versionChecker.compare(appVersion, blTargetApp[x].minVersion) < 0  ||
-              versionChecker.compare(appVersion, blTargetApp[x].maxVersion) > 0)
-            continue;
-          return true;
-        }
-      }
-
-      blTargetApp = blItem[i].targetApps[TOOLKIT_ID];
-      if (!blTargetApp)
-        return false;
-      for (x = 0; x < blTargetApp.length; ++x) {
-        if (versionChecker.compare(toolkitVersion, blTargetApp[x].minVersion) < 0  ||
-            versionChecker.compare(toolkitVersion, blTargetApp[x].maxVersion) > 0)
-          continue;
-        return true;
-      }
-    }
-    return false;
-  },
-
-  /**
    * Gets a list of items that are incompatible with a specific application version.
    * @param   appID
    *          The ID of the application - XXXben unused?
    * @param   appVersion
    *          The Version of the application to check for incompatibility against.
    * @param   desiredType
    *          The nsIUpdateItem type of items to look for
    * @param   includeDisabled
@@ -6784,32 +6414,35 @@ ExtensionsDataSource.prototype = {
    * @param   includeAppDisabled
    *          Whether or not items that are or are already set to be disabled
    *          by the app on next restart should be included in the set returned
    * @returns An array of nsIUpdateItems that are blocklisted with the application
    *          or toolkit version supplied.
    */
   getBlocklistedItemList: function(appVersion, toolkitVersion, desiredType,
                                    includeAppDisabled) {
+    if (!gBlocklist)
+      gBlocklist = Components.classes["@mozilla.org/extensions/blocklist;1"]
+                             .getService(Components.interfaces.nsIBlocklistService);
     var items = [];
     var ctr = getContainer(this._inner, this._itemRoot);
     var elements = ctr.GetElements();
     while (elements.hasMoreElements()) {
       var item = elements.getNext().QueryInterface(Components.interfaces.nsIRDFResource);
       var id = stripPrefix(item.Value, PREFIX_ITEM_URI);
       var type = this.getItemProperty(id, "type");
 
       if (!includeAppDisabled &&
           (this.getItemProperty(id, "appDisabled") == "true" ||
           this.getItemProperty(id, "appDisabled") == OP_NEEDS_DISABLE))
         continue;
 
-      var extVersion = this.getItemProperty(id, "version");
+      var version = this.getItemProperty(id, "version");
       if (type != -1 && (type & desiredType) && 
-          this.isBlocklisted(id, extVersion, appVersion, toolkitVersion))
+          gBlocklist.isAddonBlocklisted(id, version, appVersion, toolkitVersion))
         items.push(this.getItemForID(id));
     }
     return items;
   },
 
   /**
    * Gets a list of items of a specific type
    * @param   desiredType
@@ -7732,17 +7365,16 @@ ExtensionsDataSource.prototype = {
     else 
       this.visibleItems[id] = locationKey;
   },
 
   /**
    * Load the Extensions Datasource from disk.
    */
   loadExtensions: function() {
-    Blocklist._ensureBlocklist();
     var extensionsFile  = getFile(KEY_PROFILEDIR, [FILE_EXTENSIONS]);
     try {
       this._inner = gRDF.GetDataSourceBlocking(getURLSpecFromFile(extensionsFile));
     }
     catch (e) {
       LOG("Datasource::loadExtensions: removing corrupted extensions datasource " +
           " file = " + extensionsFile.path + ", exception = " + e + "\n");
       extensionsFile.remove(false);
@@ -7924,50 +7556,25 @@ ExtensionsDataSource.prototype = {
     }
     return EM_L("true");
   }, 
 
   /**
    * Get the em:blocklisted property (whether or not this item is blocklisted)
    */
   _rdfGet_blocklisted: function(item, property) {
-    Blocklist._ensureBlocklist();
     var id = stripPrefix(item.Value, PREFIX_ITEM_URI);
-    var blItem = Blocklist.entries[id];
-    if (!blItem)
-      return EM_L("false");
-
-    getVersionChecker();
     var version = this.getItemProperty(id, "version");
-    var appVersion = gApp.version;
-    for (var i = 0; i < blItem.length; ++i) {
-      if (gVersionChecker.compare(version, blItem[i].minVersion) < 0  ||
-          gVersionChecker.compare(version, blItem[i].maxVersion) > 0)
-        continue;
-
-      var blTargetApp = blItem[i].targetApps[gApp.ID];
-      if (blTargetApp) {
-        for (var x = 0; x < blTargetApp.length; ++x) {
-          if (gVersionChecker.compare(appVersion, blTargetApp[x].minVersion) < 0  ||
-              gVersionChecker.compare(appVersion, blTargetApp[x].maxVersion) > 0)
-            continue;
-          return EM_L("true");
-        }
-      }
-
-      blTargetApp = blItem[i].targetApps[TOOLKIT_ID];
-      if (!blTargetApp)
-        return EM_L("false");
-      for (x = 0; x < blTargetApp.length; ++x) {
-        if (gVersionChecker.compare(gApp.platformVersion, blTargetApp[x].minVersion) < 0  ||
-            gVersionChecker.compare(gApp.platformVersion, blTargetApp[x].maxVersion) > 0)
-          continue;
-        return EM_L("true");
-      }
-    }
+    if (!gBlocklist)
+      gBlocklist = Components.classes["@mozilla.org/extensions/blocklist;1"]
+                             .getService(Components.interfaces.nsIBlocklistService);
+    if (gBlocklist.isAddonBlocklisted(id, version,
+                                      undefined, undefined))
+      return EM_L("true");
+
     return EM_L("false");
   }, 
   
   /**
    * Get the em:state property (represents the current phase of an install).
    */
   _rdfGet_state: function(item, property) {
     var id = item.Value;