Merge mozilla-central and fx-team
authorEd Morley <emorley@mozilla.com>
Mon, 06 Jan 2014 15:20:45 +0000
changeset 179240 2068b0f801c9e3d680aeff54bddcaddebab13624
parent 179186 bafaf8d078d8e0c2f61973b54231ef23ac3c7571 (current diff)
parent 179239 70c67626470c213903c74718cf510b1bdcfe6d5e (diff)
child 179241 c467462fae8e0fb61578c19646f16f5998dc423f
push id462
push userraliiev@mozilla.com
push dateTue, 22 Apr 2014 00:22:30 +0000
treeherdermozilla-release@ac5db8c74ac0 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
milestone29.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Merge mozilla-central and fx-team
--- a/browser/base/content/aboutDialog.js
+++ b/browser/base/content/aboutDialog.js
@@ -458,17 +458,17 @@ appUpdater.prototype =
       }
     }
   },
 
   /**
    * See XPIProvider.jsm
    */
   onUpdateAvailable: function(aAddon, aInstall) {
-    if (!Services.blocklist.isAddonBlocklisted(aAddon.id, aInstall.version,
+    if (!Services.blocklist.isAddonBlocklisted(aAddon,
                                                this.update.appVersion,
                                                this.update.platformVersion)) {
       // Compatibility or new version updates mean the same thing here.
       this.onCompatibilityUpdateAvailable(aAddon);
     }
   },
 
   /**
--- a/browser/base/content/test/social/browser_blocklist.js
+++ b/browser/base/content/test/social/browser_blocklist.js
@@ -31,20 +31,20 @@ function test() {
     resetBlocklist(finish); //restore to original pref
   });
 }
 
 var tests = {
   testSimpleBlocklist: function(next) {
     // this really just tests adding and clearing our blocklist for later tests
     setAndUpdateBlocklist(blocklistURL, function() {
-      ok(Services.blocklist.isAddonBlocklisted("test1.example.com@services.mozilla.org", "0", "0", "0"), "blocking 'blocked'");
-      ok(!Services.blocklist.isAddonBlocklisted("example.com@services.mozilla.org", "0", "0", "0"), "not blocking 'good'");
+      ok(Services.blocklist.isAddonBlocklisted(SocialService.createWrapper(manifest_bad)), "blocking 'blocked'");
+      ok(!Services.blocklist.isAddonBlocklisted(SocialService.createWrapper(manifest)), "not blocking 'good'");
       resetBlocklist(function() {
-        ok(!Services.blocklist.isAddonBlocklisted("test1.example.com@services.mozilla.org", "0", "0", "0"), "blocklist cleared");
+        ok(!Services.blocklist.isAddonBlocklisted(SocialService.createWrapper(manifest_bad)), "blocklist cleared");
         next();
       });
     });
   },
   testAddingNonBlockedProvider: function(next) {
     function finish(isgood) {
       ok(isgood, "adding non-blocked provider ok");
       Services.prefs.clearUserPref("social.manifest.good");
--- a/browser/components/customizableui/src/CustomizableUI.jsm
+++ b/browser/components/customizableui/src/CustomizableUI.jsm
@@ -2084,35 +2084,83 @@ let CustomizableUIInternal = {
     }
 
     return true;
   }
 };
 Object.freeze(CustomizableUIInternal);
 
 this.CustomizableUI = {
+  /**
+   * Constant reference to the ID of the menu panel.
+   */
   get AREA_PANEL() "PanelUI-contents",
+  /**
+   * Constant reference to the ID of the navigation toolbar.
+   */
   get AREA_NAVBAR() "nav-bar",
+  /**
+   * Constant reference to the ID of the menubar's toolbar.
+   */
   get AREA_MENUBAR() "toolbar-menubar",
+  /**
+   * Constant reference to the ID of the tabstrip toolbar.
+   */
   get AREA_TABSTRIP() "TabsToolbar",
+  /**
+   * Constant reference to the ID of the bookmarks toolbar.
+   */
   get AREA_BOOKMARKS() "PersonalToolbar",
+  /**
+   * Constant reference to the ID of the addon-bar toolbar shim.
+   * Do not use, this will be removed as soon as reasonably possible.
+   * @deprecated
+   */
   get AREA_ADDONBAR() "addon-bar",
-
+  /**
+   * Constant indicating the area is a menu panel.
+   */
+  get TYPE_MENU_PANEL() "menu-panel",
+  /**
+   * Constant indicating the area is a toolbar.
+   */
+  get TYPE_TOOLBAR() "toolbar",
+
+  /**
+   * Constant indicating a XUL-type provider.
+   */
   get PROVIDER_XUL() "xul",
+  /**
+   * Constant indicating an API-type provider.
+   */
   get PROVIDER_API() "api",
+  /**
+   * Constant indicating dynamic (special) widgets: spring, spacer, and separator.
+   */
   get PROVIDER_SPECIAL() "special",
 
+  /**
+   * Constant indicating the widget is built-in
+   */
   get SOURCE_BUILTIN() "builtin",
+  /**
+   * Constant indicating the widget is externally provided
+   * (e.g. by add-ons or other items not part of the builtin widget set).
+   */
   get SOURCE_EXTERNAL() "external",
 
   get TYPE_BUTTON() "button",
-  get TYPE_MENU_PANEL() "menu-panel",
-  get TYPE_TOOLBAR() "toolbar",
-
+
+  /**
+   * The class used to distinguish items that span the entire menu panel.
+   */
   get WIDE_PANEL_CLASS() "panel-wide-item",
+  /**
+   * The (constant) number of columns in the menu panel.
+   */
   get PANEL_COLUMN_COUNT() 3,
 
   /**
    * Add a listener object that will get fired for various events regarding
    * customization.
    *
    * @param aListener the listener object to add
    *
--- a/browser/devtools/debugger/debugger.xul
+++ b/browser/devtools/debugger/debugger.xul
@@ -113,106 +113,106 @@
                 command="addWatchExpressionCommand"/>
       <menuseparator/>
       <menuitem id="cMenu_copy"/>
       <menuseparator/>
       <menuitem id="cMenu_selectAll"/>
       <menuseparator/>
       <menuitem id="se-dbg-cMenu-findFile"
                 label="&debuggerUI.searchFile;"
-                accesskey="&debuggerUI.searchFile.key;"
+                accesskey="&debuggerUI.searchFile.accesskey;"
                 key="fileSearchKey"
                 command="fileSearchCommand"/>
       <menuitem id="se-dbg-cMenu-findGlobal"
                 label="&debuggerUI.searchGlobal;"
-                accesskey="&debuggerUI.searchGlobal.key;"
+                accesskey="&debuggerUI.searchGlobal.accesskey;"
                 key="globalSearchKey"
                 command="globalSearchCommand"/>
       <menuitem id="se-dbg-cMenu-findFunction"
                 label="&debuggerUI.searchFunction;"
-                accesskey="&debuggerUI.searchFunction.key;"
+                accesskey="&debuggerUI.searchFunction.accesskey;"
                 key="functionSearchKey"
                 command="functionSearchCommand"/>
       <menuseparator/>
       <menuitem id="se-dbg-cMenu-findToken"
                 label="&debuggerUI.searchToken;"
-                accesskey="&debuggerUI.searchToken.key;"
+                accesskey="&debuggerUI.searchToken.accesskey;"
                 key="tokenSearchKey"
                 command="tokenSearchCommand"/>
       <menuitem id="se-dbg-cMenu-findLine"
                 label="&debuggerUI.searchGoToLine;"
-                accesskey="&debuggerUI.searchGoToLine.key;"
+                accesskey="&debuggerUI.searchGoToLine.accesskey;"
                 key="lineSearchKey"
                 command="lineSearchCommand"/>
       <menuseparator/>
       <menuitem id="se-dbg-cMenu-findVariable"
                 label="&debuggerUI.searchVariable;"
-                accesskey="&debuggerUI.searchVariable.key;"
+                accesskey="&debuggerUI.searchVariable.accesskey;"
                 key="variableSearchKey"
                 command="variableSearchCommand"/>
       <menuitem id="se-dbg-cMenu-focusVariables"
                 label="&debuggerUI.focusVariables;"
-                accesskey="&debuggerUI.focusVariables.key;"
+                accesskey="&debuggerUI.focusVariables.accesskey;"
                 key="variablesFocusKey"
                 command="variablesFocusCommand"/>
       <menuitem id="se-dbg-cMenu-prettyPrint"
                 label="&debuggerUI.sources.prettyPrint;"
                 command="prettyPrintCommand"/>
     </menupopup>
     <menupopup id="debuggerWatchExpressionsContextMenu">
       <menuitem id="add-watch-expression"
                 label="&debuggerUI.addWatch;"
-                accesskey="&debuggerUI.addWatch.key;"
+                accesskey="&debuggerUI.addWatch.accesskey;"
                 key="addWatchExpressionKey"
                 command="addWatchExpressionCommand"/>
       <menuitem id="removeAll-watch-expression"
                 label="&debuggerUI.removeAllWatch;"
-                accesskey="&debuggerUI.removeAllWatch.key;"
+                accesskey="&debuggerUI.removeAllWatch.accesskey;"
                 key="removeAllWatchExpressionsKey"
                 command="removeAllWatchExpressionsCommand"/>
     </menupopup>
     <menupopup id="debuggerPrefsContextMenu"
                position="before_end"
                onpopupshowing="DebuggerView.Options._onPopupShowing()"
                onpopuphiding="DebuggerView.Options._onPopupHiding()"
                onpopuphidden="DebuggerView.Options._onPopupHidden()">
       <menuitem id="auto-pretty-print"
                 type="checkbox"
                 label="&debuggerUI.autoPrettyPrint;"
                 accesskey="&debuggerUI.autoPrettyPrint.key;"
                 command="toggleAutoPrettyPrint"/>
       <menuitem id="pause-on-exceptions"
                 type="checkbox"
                 label="&debuggerUI.pauseExceptions;"
-                accesskey="&debuggerUI.pauseExceptions.key;"
+                accesskey="&debuggerUI.pauseExceptions.accesskey;"
                 command="togglePauseOnExceptions"/>
       <menuitem id="ignore-caught-exceptions"
                 type="checkbox"
                 label="&debuggerUI.ignoreCaughtExceptions;"
-                accesskey="&debuggerUI.ignoreCaughtExceptions.key;"
+                accesskey="&debuggerUI.ignoreCaughtExceptions.accesskey;"
                 command="toggleIgnoreCaughtExceptions"/>
       <menuitem id="show-panes-on-startup"
                 type="checkbox"
                 label="&debuggerUI.showPanesOnInit;"
-                accesskey="&debuggerUI.showPanesOnInit.key;"
+                accesskey="&debuggerUI.showPanesOnInit.accesskey;"
                 command="toggleShowPanesOnStartup"/>
       <menuitem id="show-vars-only-enum"
                 type="checkbox"
                 label="&debuggerUI.showOnlyEnum;"
-                accesskey="&debuggerUI.showOnlyEnum.key;"
+                accesskey="&debuggerUI.showOnlyEnum.accesskey;"
                 command="toggleShowOnlyEnum"/>
       <menuitem id="show-vars-filter-box"
                 type="checkbox"
                 label="&debuggerUI.showVarsFilter;"
-                accesskey="&debuggerUI.showVarsFilter.key;"
+                accesskey="&debuggerUI.showVarsFilter.accesskey;"
                 command="toggleShowVariablesFilterBox"/>
       <menuitem id="show-original-source"
                 type="checkbox"
                 label="&debuggerUI.showOriginalSource;"
-                accesskey="&debuggerUI.showOriginalSource.key;"
+                accesskey="&debuggerUI.showOriginalSource.accesskey;"
                 command="toggleShowOriginalSource"/>
     </menupopup>
   </popupset>
 
   <keyset id="debuggerKeys">
     <key id="nextSourceKey"
          keycode="VK_DOWN"
          modifiers="accel alt"
--- a/browser/devtools/styleeditor/StyleEditorUtil.jsm
+++ b/browser/devtools/styleeditor/StyleEditorUtil.jsm
@@ -167,18 +167,21 @@ this.wire = function wire(aRoot, aSelect
  * @param boolean toSave
  *        If true, the user is selecting a filename to save.
  * @param nsIWindow parentWindow
  *        Optional parent window. If null the parent window of the file picker
  *        will be the window of the attached input element.
  * @param callback
  *        The callback method, which will be called passing in the selected
  *        file or null if the user did not pick one.
+ * @param AString suggestedFilename
+ *        The suggested filename when toSave is true.
  */
-this.showFilePicker = function showFilePicker(path, toSave, parentWindow, callback)
+this.showFilePicker = function showFilePicker(path, toSave, parentWindow,
+                                              callback, suggestedFilename)
 {
   if (typeof(path) == "string") {
     try {
       if (Services.io.extractScheme(path) == "file") {
         let uri = Services.io.newURI(path, null, null);
         let file = uri.QueryInterface(Ci.nsIFileURL).file;
         callback(file);
         return;
@@ -208,14 +211,18 @@ this.showFilePicker = function showFileP
   let fpCallback = function(result) {
     if (result == Ci.nsIFilePicker.returnCancel) {
       callback(null);
     } else {
       callback(fp.file);
     }
   };
 
+  if (toSave && suggestedFilename) {
+    fp.defaultString = suggestedFilename;
+  }
+
   fp.init(parentWindow, _(key + ".title"), mode);
   fp.appendFilters(_(key + ".filter"), "*.css");
   fp.appendFilters(fp.filterAll);
   fp.open(fpCallback);
   return;
 }
--- a/browser/devtools/styleeditor/StyleSheetEditor.jsm
+++ b/browser/devtools/styleeditor/StyleSheetEditor.jsm
@@ -14,16 +14,17 @@ const Cu = Components.utils;
 const require = Cu.import("resource://gre/modules/devtools/Loader.jsm", {}).devtools.require;
 const Editor  = require("devtools/sourceeditor/editor");
 const promise = require("sdk/core/promise");
 const {CssLogic} = require("devtools/styleinspector/css-logic");
 
 Cu.import("resource://gre/modules/Services.jsm");
 Cu.import("resource://gre/modules/FileUtils.jsm");
 Cu.import("resource://gre/modules/NetUtil.jsm");
+Cu.import("resource://gre/modules/osfile.jsm");
 Cu.import("resource:///modules/devtools/shared/event-emitter.js");
 Cu.import("resource:///modules/devtools/StyleEditorUtil.jsm");
 
 const LOAD_ERROR = "error-load";
 const SAVE_ERROR = "error-save";
 
 // max update frequency in ms (avoid potential typing lag and/or flicker)
 // @see StyleEditor.updateStylesheet
@@ -354,18 +355,23 @@ StyleSheetEditor.prototype = {
           callback(returnFile);
         }
         this.sourceEditor.setClean();
 
         this.emit("property-change");
       }.bind(this));
     };
 
-    showFilePicker(file || this._styleSheetFilePath, true, this._window, onFile);
-  },
+    let defaultName;
+    if (this._friendlyName) {
+      defaultName = OS.Path.basename(this._friendlyName);
+    }
+    showFilePicker(file || this._styleSheetFilePath, true, this._window,
+                   onFile, defaultName);
+ },
 
   /**
     * Retrieve custom key bindings objects as expected by Editor.
     * Editor action names are not displayed to the user.
     *
     * @return {array} key binding objects for the source editor
     */
   _getKeyBindings: function() {
@@ -389,18 +395,18 @@ StyleSheetEditor.prototype = {
     this.styleSheet.off("property-change", this._onPropertyChange);
     this.styleSheet.off("error", this._onError);
   }
 }
 
 
 const TAB_CHARS = "\t";
 
-const OS = Cc["@mozilla.org/xre/app-info;1"].getService(Ci.nsIXULRuntime).OS;
-const LINE_SEPARATOR = OS === "WINNT" ? "\r\n" : "\n";
+const CURRENT_OS = Cc["@mozilla.org/xre/app-info;1"].getService(Ci.nsIXULRuntime).OS;
+const LINE_SEPARATOR = CURRENT_OS === "WINNT" ? "\r\n" : "\n";
 
 /**
  * Prettify minified CSS text.
  * This prettifies CSS code where there is no indentation in usual places while
  * keeping original indentation as-is elsewhere.
  *
  * @param string text
  *        The CSS source to prettify.
--- a/browser/locales/en-US/chrome/browser/devtools/debugger.dtd
+++ b/browser/locales/en-US/chrome/browser/devtools/debugger.dtd
@@ -63,95 +63,102 @@
 <!ENTITY debuggerUI.clearButton "Clear">
 
 <!-- LOCALIZATION NOTE (debuggerUI.clearButton.tooltip): This is the tooltip for
   -  the button that clears the collected tracing data in the tracing tab. -->
 <!ENTITY debuggerUI.clearButton.tooltip "Clear the collected traces">
 
 <!-- LOCALIZATION NOTE (debuggerUI.pauseExceptions): This is the label for the
   -  checkbox that toggles pausing on exceptions. -->
-<!ENTITY debuggerUI.pauseExceptions     "Pause on exceptions">
-<!ENTITY debuggerUI.pauseExceptions.key "E">
+<!ENTITY debuggerUI.pauseExceptions           "Pause on exceptions">
+<!ENTITY debuggerUI.pauseExceptions.accesskey "E">
 
 <!-- LOCALIZATION NOTE (debuggerUI.ignoreCaughtExceptions): This is the label for the
   -  checkbox that toggles ignoring caught exceptions. -->
-<!ENTITY debuggerUI.ignoreCaughtExceptions     "Ignore caught exceptions">
-<!ENTITY debuggerUI.ignoreCaughtExceptions.key "C">
+<!ENTITY debuggerUI.ignoreCaughtExceptions           "Ignore caught exceptions">
+<!ENTITY debuggerUI.ignoreCaughtExceptions.accesskey "C">
 
 <!-- LOCALIZATION NOTE (debuggerUI.showPanesOnInit): This is the label for the
   -  checkbox that toggles visibility of panes when opening the debugger. -->
-<!ENTITY debuggerUI.showPanesOnInit     "Show panes on startup">
-<!ENTITY debuggerUI.showPanesOnInit.key "S">
+<!ENTITY debuggerUI.showPanesOnInit           "Show panes on startup">
+<!ENTITY debuggerUI.showPanesOnInit.accesskey "S">
 
 <!-- LOCALIZATION NOTE (debuggerUI.showVarsFilter): This is the label for the
   -  checkbox that toggles visibility of a designated variables filter box. -->
-<!ENTITY debuggerUI.showVarsFilter      "Show variables filter box">
-<!ENTITY debuggerUI.showVarsFilter.key  "V">
+<!ENTITY debuggerUI.showVarsFilter           "Show variables filter box">
+<!ENTITY debuggerUI.showVarsFilter.accesskey "V">
 
 <!-- LOCALIZATION NOTE (debuggerUI.showOnlyEnum): This is the label for the
   -  checkbox that toggles visibility of hidden (non-enumerable) variables and
   -  properties in stack views. The "enumerable" flag is a state of a property
   -  defined in JavaScript. When in doubt, leave untranslated. -->
-<!ENTITY debuggerUI.showOnlyEnum        "Show only enumerable properties">
-<!ENTITY debuggerUI.showOnlyEnum.key    "P">
+<!ENTITY debuggerUI.showOnlyEnum           "Show only enumerable properties">
+<!ENTITY debuggerUI.showOnlyEnum.accesskey "P">
 
 <!-- LOCALIZATION NOTE (debuggerUI.showOriginalSource): This is the label for
   -  the checkbox that toggles the display of original or sourcemap-derived
   -  sources. -->
-<!ENTITY debuggerUI.showOriginalSource      "Show original sources">
-<!ENTITY debuggerUI.showOriginalSource.key  "O">
+<!ENTITY debuggerUI.showOriginalSource           "Show original sources">
+<!ENTITY debuggerUI.showOriginalSource.accesskey "O">
 
 <!-- LOCALIZATION NOTE (debuggerUI.searchPanelOperators): This is the text that
   -  appears in the filter panel popup as a header for the operators part. -->
 <!ENTITY debuggerUI.searchPanelOperators    "Operators:">
 
 <!-- LOCALIZATION NOTE (debuggerUI.searchFile): This is the text that appears
   -  in the source editor's context menu for the scripts search operation. -->
-<!ENTITY debuggerUI.searchFile          "Filter scripts">
-<!ENTITY debuggerUI.searchFile.key      "P">
-<!ENTITY debuggerUI.searchFile.altkey   "O">
+<!ENTITY debuggerUI.searchFile           "Filter scripts">
+<!ENTITY debuggerUI.searchFile.key       "P">
+<!ENTITY debuggerUI.searchFile.altkey    "O">
+<!ENTITY debuggerUI.searchFile.accesskey "P">
 
 <!-- LOCALIZATION NOTE (debuggerUI.searchGlobal): This is the text that appears
   -  in the source editor's context menu for the global search operation. -->
-<!ENTITY debuggerUI.searchGlobal        "Search in all files">
-<!ENTITY debuggerUI.searchGlobal.key    "F">
+<!ENTITY debuggerUI.searchGlobal           "Search in all files">
+<!ENTITY debuggerUI.searchGlobal.key       "F">
+<!ENTITY debuggerUI.searchGlobal.accesskey "F">
 
 <!-- LOCALIZATION NOTE (debuggerUI.searchFunction): This is the text that appears
   -  in the source editor's context menu for the function search operation. -->
-<!ENTITY debuggerUI.searchFunction      "Search for function definition">
-<!ENTITY debuggerUI.searchFunction.key  "D">
+<!ENTITY debuggerUI.searchFunction           "Search for function definition">
+<!ENTITY debuggerUI.searchFunction.key       "D">
+<!ENTITY debuggerUI.searchFunction.accesskey "D">
 
 <!-- LOCALIZATION NOTE (debuggerUI.searchToken): This is the text that appears
   -  in the source editor's context menu for the token search operation. -->
-<!ENTITY debuggerUI.searchToken         "Find">
-<!ENTITY debuggerUI.searchToken.key     "F">
+<!ENTITY debuggerUI.searchToken           "Find">
+<!ENTITY debuggerUI.searchToken.key       "F">
+<!ENTITY debuggerUI.searchToken.accesskey "F">
 
 <!-- LOCALIZATION NOTE (debuggerUI.searchLine): This is the text that appears
   -  in the source editor's context menu for the line search operation. -->
-<!ENTITY debuggerUI.searchGoToLine      "Go to line…">
-<!ENTITY debuggerUI.searchGoToLine.key  "L">
+<!ENTITY debuggerUI.searchGoToLine           "Go to line…">
+<!ENTITY debuggerUI.searchGoToLine.key       "L">
+<!ENTITY debuggerUI.searchGoToLine.accesskey "L">
 
 <!-- LOCALIZATION NOTE (debuggerUI.searchVariable): This is the text that appears
   -  in the source editor's context menu for the variables search operation. -->
-<!ENTITY debuggerUI.searchVariable      "Filter variables">
-<!ENTITY debuggerUI.searchVariable.key  "V">
+<!ENTITY debuggerUI.searchVariable           "Filter variables">
+<!ENTITY debuggerUI.searchVariable.key       "V">
+<!ENTITY debuggerUI.searchVariable.accesskey "V">
 
 <!-- LOCALIZATION NOTE (debuggerUI.focusVariables): This is the text that appears
   -  in the source editor's context menu for the variables focus operation. -->
-<!ENTITY debuggerUI.focusVariables      "Focus variables tree">
-<!ENTITY debuggerUI.focusVariables.key  "V">
+<!ENTITY debuggerUI.focusVariables           "Focus variables tree">
+<!ENTITY debuggerUI.focusVariables.key       "V">
+<!ENTITY debuggerUI.focusVariables.accesskey "V">
 
 <!-- LOCALIZATION NOTE (debuggerUI.condBreakPanelTitle): This is the text that
   -  appears in the conditional breakpoint panel popup as a description. -->
 <!ENTITY debuggerUI.condBreakPanelTitle "This breakpoint will stop execution only if the following expression is true">
 
 <!-- LOCALIZATION NOTE (debuggerUI.seMenuBreak): This is the text that
   -  appears in the source editor context menu for adding a breakpoint. -->
-<!ENTITY debuggerUI.seMenuBreak         "Add breakpoint">
-<!ENTITY debuggerUI.seMenuBreak.key     "B">
+<!ENTITY debuggerUI.seMenuBreak     "Add breakpoint">
+<!ENTITY debuggerUI.seMenuBreak.key "B">
 
 <!-- LOCALIZATION NOTE (debuggerUI.seMenuCondBreak): This is the text that
   -  appears in the source editor context menu for adding a conditional
   -  breakpoint. -->
 <!ENTITY debuggerUI.seMenuCondBreak     "Add conditional breakpoint">
 <!ENTITY debuggerUI.seMenuCondBreak.key "B">
 
 <!-- LOCALIZATION NOTE (debuggerUI.tabs.*): This is the text that
@@ -165,22 +172,23 @@
 <!-- LOCALIZATION NOTE (debuggerUI.seMenuAddWatch): This is the text that
   -  appears in the source editor context menu for adding an expression. -->
 <!ENTITY debuggerUI.seMenuAddWatch      "Selection to watch expression">
 <!ENTITY debuggerUI.seMenuAddWatch.key  "E">
 
 <!-- LOCALIZATION NOTE (debuggerUI.addWatch): This is the text that
   -  appears in the watch expressions context menu for adding an expression. -->
 <!ENTITY debuggerUI.addWatch            "Add watch expression">
-<!ENTITY debuggerUI.addWatch.key        "E">
+<!ENTITY debuggerUI.addWatch.accesskey  "E">
 
 <!-- LOCALIZATION NOTE (debuggerUI.removeWatch): This is the text that
   -  appears in the watch expressions context menu for removing all expressions. -->
-<!ENTITY debuggerUI.removeAllWatch      "Remove all watch expressions">
-<!ENTITY debuggerUI.removeAllWatch.key  "E">
+<!ENTITY debuggerUI.removeAllWatch           "Remove all watch expressions">
+<!ENTITY debuggerUI.removeAllWatch.key       "E">
+<!ENTITY debuggerUI.removeAllWatch.accesskey "E">
 
 <!-- LOCALIZATION NOTE (debuggerUI.stepping): These are the keycodes that
   -  control the stepping commands in the debugger (continue, step over,
   -  step in and step out). -->
 <!ENTITY debuggerUI.stepping.resume1    "VK_F8">
 <!ENTITY debuggerUI.stepping.resume2    "VK_SLASH">
 <!ENTITY debuggerUI.stepping.stepOver1  "VK_F10">
 <!ENTITY debuggerUI.stepping.stepOver2  "VK_QUOTE">
--- a/browser/metro/base/content/flyoutpanels/AboutFlyoutPanel.js
+++ b/browser/metro/base/content/flyoutpanels/AboutFlyoutPanel.js
@@ -449,17 +449,17 @@ appUpdater.prototype =
       }
     }
   },
 
   /**
    * See XPIProvider.jsm
    */
   onUpdateAvailable: function(aAddon, aInstall) {
-    if (!Services.blocklist.isAddonBlocklisted(aAddon.id, aInstall.version,
+    if (!Services.blocklist.isAddonBlocklisted(aAddon,
                                                this.update.appVersion,
                                                this.update.platformVersion)) {
       // Compatibility or new version updates mean the same thing here.
       this.onCompatibilityUpdateAvailable(aAddon);
     }
   },
 
   /**
--- a/mobile/android/chrome/content/browser.js
+++ b/mobile/android/chrome/content/browser.js
@@ -3212,30 +3212,16 @@ Tab.prototype = {
     let x = aViewport.x / aViewport.zoom;
     let y = aViewport.y / aViewport.zoom;
 
     this.setScrollClampingSize(aViewport.zoom);
 
     // Adjust the max line box width to be no more than the viewport width, but
     // only if the reflow-on-zoom preference is enabled.
     let isZooming = !fuzzyEquals(aViewport.zoom, this._zoom);
-    if (BrowserApp.selectedTab.reflozPinchSeen &&
-        isZooming && aViewport.zoom < 1.0) {
-      // In this case, we want to restore the max line box width,
-      // because we are pinch-zooming to zoom out.
-      BrowserEventHandler.resetMaxLineBoxWidth();
-      BrowserApp.selectedTab.reflozPinchSeen = false;
-    } else if (BrowserApp.selectedTab.reflozPinchSeen &&
-               isZooming) {
-      // In this case, the user pinch-zoomed in, so we don't want to
-      // preserve position as we would with reflow-on-zoom.
-      BrowserApp.selectedTab.probablyNeedRefloz = false;
-      BrowserApp.selectedTab.clearReflowOnZoomPendingActions();
-      BrowserApp.selectedTab._mReflozPoint = null;
-    }
 
     let docViewer = null;
 
     if (isZooming &&
         BrowserEventHandler.mReflozPref &&
         BrowserApp.selectedTab._mReflozPoint &&
         BrowserApp.selectedTab.probablyNeedRefloz) {
       let webNav = BrowserApp.selectedTab.window.QueryInterface(Ci.nsIInterfaceRequestor).getInterface(Ci.nsIWebNavigation);
--- a/toolkit/components/social/SocialService.jsm
+++ b/toolkit/components/social/SocialService.jsm
@@ -582,22 +582,22 @@ this.SocialService = {
     let notificationid = "servicesInstall";
     chromeWin.PopupNotifications.show(browser, notificationid, message, anchor,
                                       action, [], {});
   },
 
   installProvider: function(aDOMDocument, data, installCallback) {
     let installOrigin = aDOMDocument.nodePrincipal.origin;
 
-    let id = getAddonIDFromOrigin(installOrigin);
-    let version = data && data.version ? data.version : "0";
-    if (Services.blocklist.getAddonBlocklistState(id, version) == Ci.nsIBlocklistService.STATE_BLOCKED)
+    let addon = new AddonWrapper(data);
+    if (addon.blocklistState == Ci.nsIBlocklistService.STATE_BLOCKED)
       throw new Error("installProvider: provider with origin [" +
                       installOrigin + "] is blocklisted");
 
+    let id = getAddonIDFromOrigin(installOrigin);
     AddonManager.getAddonByID(id, function(aAddon) {
       if (aAddon && aAddon.userDisabled) {
         aAddon.cancelUninstall();
         aAddon.userDisabled = false;
       }
       schedule(function () {
         this._installProvider(aDOMDocument, data, aManifest => {
           this._notifyProviderListeners("provider-installed", aManifest.origin);
@@ -657,16 +657,20 @@ this.SocialService = {
         this._showInstallNotification(aDOMDocument, installer);
         break;
       default:
         throw new Error("SocialService.installProvider: Invalid install type "+installType+"\n");
         break;
     }
   },
 
+  createWrapper: function(manifest) {
+    return new AddonWrapper(manifest);
+  },
+
   /**
    * updateProvider is used from the worker to self-update.  Since we do not
    * have knowledge of the currently selected provider here, we will notify
    * the front end to deal with any reload.
    */
   updateProvider: function(aUpdateOrigin, aManifest) {
     let originUri = Services.io.newURI(aUpdateOrigin, null, null);
     let principal = Services.scriptSecurityManager.getNoAppCodebasePrincipal(originUri);
@@ -711,18 +715,18 @@ this.SocialService = {
  * @param {bool} boolean indicating whether this provider is "built in"
  */
 function SocialProvider(input) {
   if (!input.name)
     throw new Error("SocialProvider must be passed a name");
   if (!input.origin)
     throw new Error("SocialProvider must be passed an origin");
 
-  let id = getAddonIDFromOrigin(input.origin);
-  if (Services.blocklist.getAddonBlocklistState(id, input.version || "0") == Ci.nsIBlocklistService.STATE_BLOCKED)
+  let addon = new AddonWrapper(input);
+  if (addon.blocklistState == Ci.nsIBlocklistService.STATE_BLOCKED)
     throw new Error("SocialProvider: provider with origin [" +
                     input.origin + "] is blocklisted");
 
   this.name = input.name;
   this.iconURL = input.iconURL;
   this.icon32URL = input.icon32URL;
   this.icon64URL = input.icon64URL;
   this.workerURL = input.workerURL;
@@ -1006,18 +1010,18 @@ var SocialAddonProvider = {
 
   shutdown: function() {},
 
   updateAddonAppDisabledStates: function() {
     // we wont bother with "enabling" services that are released from blocklist
     for (let manifest of SocialServiceInternal.manifests) {
       try {
         if (ActiveProviders.has(manifest.origin)) {
-          let id = getAddonIDFromOrigin(manifest.origin);
-          if (Services.blocklist.getAddonBlocklistState(id, manifest.version || "0") != Ci.nsIBlocklistService.STATE_NOT_BLOCKED) {
+          let addon = new AddonWrapper(manifest);
+          if (addon.blocklistState != Ci.nsIBlocklistService.STATE_NOT_BLOCKED) {
             SocialService.removeProvider(manifest.origin);
           }
         }
       } catch(e) {
         Cu.reportError(e);
       }
     }
   },
@@ -1095,21 +1099,21 @@ AddonWrapper.prototype = {
     return true;
   },
 
   get providesUpdatesSecurely() {
     return true;
   },
 
   get blocklistState() {
-    return Services.blocklist.getAddonBlocklistState(this.id, this.version || "0");
+    return Services.blocklist.getAddonBlocklistState(this);
   },
 
   get blocklistURL() {
-    return Services.blocklist.getAddonBlocklistURL(this.id, this.version || "0");
+    return Services.blocklist.getAddonBlocklistURL(this);
   },
 
   get screenshots() {
     return [];
   },
 
   get pendingOperations() {
     return this._pending || AddonManager.PENDING_NONE;
--- a/toolkit/components/url-classifier/Entries.h
+++ b/toolkit/components/url-classifier/Entries.h
@@ -235,20 +235,20 @@ struct SubComplete {
     if (cmp != 0)
       return cmp;
     if (addChunk != aOther.addChunk)
       return addChunk - aOther.addChunk;
     return subChunk - aOther.subChunk;
   }
 };
 
-typedef nsTArray<AddPrefix>   AddPrefixArray;
-typedef nsTArray<AddComplete> AddCompleteArray;
-typedef nsTArray<SubPrefix>   SubPrefixArray;
-typedef nsTArray<SubComplete> SubCompleteArray;
+typedef FallibleTArray<AddPrefix>   AddPrefixArray;
+typedef FallibleTArray<AddComplete> AddCompleteArray;
+typedef FallibleTArray<SubPrefix>   SubPrefixArray;
+typedef FallibleTArray<SubComplete> SubCompleteArray;
 
 /**
  * Compares chunks by their add chunk, then their prefix.
  */
 template<class T>
 class EntryCompare {
 public:
   typedef T elem_type;
@@ -259,41 +259,41 @@ public:
   }
 };
 
 /**
  * Sort an array of store entries.  nsTArray::Sort uses Equal/LessThan
  * to sort, this does a single Compare so it's a bit quicker over the
  * large sorts we do.
  */
-template<class T>
+template<class T, class Alloc>
 void
-EntrySort(nsTArray<T>& aArray)
+EntrySort(nsTArray_Impl<T, Alloc>& aArray)
 {
   qsort(aArray.Elements(), aArray.Length(), sizeof(T),
         EntryCompare<T>::Compare);
 }
 
-template<class T>
+template<class T, class Alloc>
 nsresult
-ReadTArray(nsIInputStream* aStream, nsTArray<T>* aArray, uint32_t aNumElements)
+ReadTArray(nsIInputStream* aStream, nsTArray_Impl<T, Alloc>* aArray, uint32_t aNumElements)
 {
   if (!aArray->SetLength(aNumElements))
     return NS_ERROR_OUT_OF_MEMORY;
 
   void *buffer = aArray->Elements();
   nsresult rv = NS_ReadInputStreamToBuffer(aStream, &buffer,
                                            (aNumElements * sizeof(T)));
   NS_ENSURE_SUCCESS(rv, rv);
   return NS_OK;
 }
 
-template<class T>
+template<class T, class Alloc>
 nsresult
-WriteTArray(nsIOutputStream* aStream, nsTArray<T>& aArray)
+WriteTArray(nsIOutputStream* aStream, nsTArray_Impl<T, Alloc>& aArray)
 {
   uint32_t written;
   return aStream->Write(reinterpret_cast<char*>(aArray.Elements()),
                         aArray.Length() * sizeof(T),
                         &written);
 }
 
 } // namespace safebrowsing
--- a/toolkit/components/url-classifier/HashStore.cpp
+++ b/toolkit/components/url-classifier/HashStore.cpp
@@ -428,19 +428,19 @@ HashStore::BeginUpdate()
   mInUpdate = true;
 
   return NS_OK;
 }
 
 template<class T>
 static nsresult
 Merge(ChunkSet* aStoreChunks,
-      nsTArray<T>* aStorePrefixes,
+      FallibleTArray<T>* aStorePrefixes,
       ChunkSet& aUpdateChunks,
-      nsTArray<T>& aUpdatePrefixes,
+      FallibleTArray<T>& aUpdatePrefixes,
       bool aAllowMerging = false)
 {
   EntrySort(aUpdatePrefixes);
 
   T* updateIter = aUpdatePrefixes.Elements();
   T* updateEnd = aUpdatePrefixes.Elements() + aUpdatePrefixes.Length();
 
   T* storeIter = aStorePrefixes->Elements();
@@ -533,17 +533,17 @@ HashStore::ClearCompletes()
   mAddCompletes.Clear();
   mSubCompletes.Clear();
 
   UpdateHeader();
 }
 
 template<class T>
 static void
-ExpireEntries(nsTArray<T>* aEntries, ChunkSet& aExpirations)
+ExpireEntries(FallibleTArray<T>* aEntries, ChunkSet& aExpirations)
 {
   T* addIter = aEntries->Elements();
   T* end = aEntries->Elements() + aEntries->Length();
 
   for (T *iter = addIter; iter != end; iter++) {
     if (!aExpirations.Has(iter->Chunk())) {
       *addIter = *iter;
       addIter++;
@@ -570,18 +570,20 @@ HashStore::Expire()
   return NS_OK;
 }
 
 template<class T>
 nsresult DeflateWriteTArray(nsIOutputStream* aStream, nsTArray<T>& aIn)
 {
   uLongf insize = aIn.Length() * sizeof(T);
   uLongf outsize = compressBound(insize);
-  nsTArray<char> outBuff;
-  outBuff.SetLength(outsize);
+  FallibleTArray<char> outBuff;
+  if (!outBuff.SetLength(outsize)) {
+    return NS_ERROR_OUT_OF_MEMORY;
+  }
 
   int zerr = compress(reinterpret_cast<Bytef*>(outBuff.Elements()),
                       &outsize,
                       reinterpret_cast<const Bytef*>(aIn.Elements()),
                       insize);
   if (zerr != Z_OK) {
     return NS_ERROR_FAILURE;
   }
@@ -711,17 +713,19 @@ nsresult
 HashStore::ReadAddPrefixes()
 {
   FallibleTArray<uint32_t> chunks;
   uint32_t count = mHeader.numAddPrefixes;
 
   nsresult rv = ByteSliceRead(mInputStream, &chunks, count);
   NS_ENSURE_SUCCESS(rv, rv);
 
-  mAddPrefixes.SetCapacity(count);
+  if (!mAddPrefixes.SetCapacity(count)) {
+    return NS_ERROR_OUT_OF_MEMORY;
+  }
   for (uint32_t i = 0; i < count; i++) {
     AddPrefix *add = mAddPrefixes.AppendElement();
     add->prefix.FromUint32(0);
     add->addChunk = chunks[i];
   }
 
   return NS_OK;
 }
@@ -738,17 +742,19 @@ HashStore::ReadSubPrefixes()
   NS_ENSURE_SUCCESS(rv, rv);
 
   rv = ByteSliceRead(mInputStream, &subchunks, count);
   NS_ENSURE_SUCCESS(rv, rv);
 
   rv = ByteSliceRead(mInputStream, &prefixes, count);
   NS_ENSURE_SUCCESS(rv, rv);
 
-  mSubPrefixes.SetCapacity(count);
+  if (!mSubPrefixes.SetCapacity(count)) {
+    return NS_ERROR_OUT_OF_MEMORY;
+  }
   for (uint32_t i = 0; i < count; i++) {
     SubPrefix *sub = mSubPrefixes.AppendElement();
     sub->addChunk = addchunks[i];
     sub->prefix.FromUint32(prefixes[i]);
     sub->subChunk = subchunks[i];
   }
 
   return NS_OK;
@@ -847,17 +853,17 @@ HashStore::WriteFile()
   rv = safeOut->Finish();
   NS_ENSURE_SUCCESS(rv, rv);
 
   return NS_OK;
 }
 
 template <class T>
 static void
-Erase(nsTArray<T>* array, T* iterStart, T* iterEnd)
+Erase(FallibleTArray<T>* array, T* iterStart, T* iterEnd)
 {
   uint32_t start = iterStart - array->Elements();
   uint32_t count = iterEnd - iterStart;
 
   if (count > 0) {
     array->RemoveElementsAt(start, count);
   }
 }
@@ -867,17 +873,17 @@ Erase(nsTArray<T>* array, T* iterStart, 
 // copies, the inputs are processing in parallel, so |subs| and |adds|
 // should be compatibly ordered (either by SBAddPrefixLess or
 // SBAddPrefixHashLess).
 //
 // |predAS| provides add < sub, |predSA| provides sub < add, for the
 // tightest compare appropriate (see calls in SBProcessSubs).
 template<class TSub, class TAdd>
 static void
-KnockoutSubs(nsTArray<TSub>* aSubs, nsTArray<TAdd>* aAdds)
+KnockoutSubs(FallibleTArray<TSub>* aSubs, FallibleTArray<TAdd>* aAdds)
 {
   // Keep a pair of output iterators for writing kept items.  Due to
   // deletions, these may lag the main iterators.  Using erase() on
   // individual items would result in O(N^2) copies.  Using a list
   // would work around that, at double or triple the memory cost.
   TAdd* addOut = aAdds->Elements();
   TAdd* addIter = aAdds->Elements();
 
@@ -910,17 +916,17 @@ KnockoutSubs(nsTArray<TSub>* aSubs, nsTA
   Erase(aAdds, addOut, addIter);
   Erase(aSubs, subOut, subIter);
 }
 
 // Remove items in |removes| from |fullHashes|.  |fullHashes| and
 // |removes| should be ordered by SBAddPrefix component.
 template <class T>
 static void
-RemoveMatchingPrefixes(const SubPrefixArray& aSubs, nsTArray<T>* aFullHashes)
+RemoveMatchingPrefixes(const SubPrefixArray& aSubs, FallibleTArray<T>* aFullHashes)
 {
   // Where to store kept items.
   T* out = aFullHashes->Elements();
   T* hashIter = out;
   T* hashEnd = aFullHashes->Elements() + aFullHashes->Length();
 
   SubPrefix const * removeIter = aSubs.Elements();
   SubPrefix const * removeEnd = aSubs.Elements() + aSubs.Length();
--- a/toolkit/mozapps/extensions/AddonUpdateChecker.jsm
+++ b/toolkit/mozapps/extensions/AddonUpdateChecker.jsm
@@ -716,18 +716,17 @@ this.AddonUpdateChecker = {
 
     let blocklist = Cc["@mozilla.org/extensions/blocklist;1"].
                     getService(Ci.nsIBlocklistService);
 
     let newest = null;
     for (let update of aUpdates) {
       if (!update.updateURL)
         continue;
-      let state = blocklist.getAddonBlocklistState(update.id, update.version,
-                                                   aAppVersion, aPlatformVersion);
+      let state = blocklist.getAddonBlocklistState(update, aAppVersion, aPlatformVersion);
       if (state != Ci.nsIBlocklistService.STATE_NOT_BLOCKED)
         continue;
       if ((newest == null || (Services.vc.compare(newest.version, update.version) < 0)) &&
           matchesVersions(update, aAppVersion, aPlatformVersion,
                           aIgnoreMaxVersion, aIgnoreStrictCompat,
                           aCompatOverrides)) {
         newest = update;
       }
--- a/toolkit/mozapps/extensions/XPIProvider.jsm
+++ b/toolkit/mozapps/extensions/XPIProvider.jsm
@@ -545,22 +545,20 @@ function applyBlocklistChanges(aOldAddon
                                aOldPlatformVersion) {
   // Copy the properties by default
   aNewAddon.userDisabled = aOldAddon.userDisabled;
   aNewAddon.softDisabled = aOldAddon.softDisabled;
 
   let bs = Cc["@mozilla.org/extensions/blocklist;1"].
            getService(Ci.nsIBlocklistService);
 
-  let oldBlocklistState = bs.getAddonBlocklistState(aOldAddon.id,
-                                                    aOldAddon.version,
+  let oldBlocklistState = bs.getAddonBlocklistState(createWrapper(aOldAddon),
                                                     aOldAppVersion,
                                                     aOldPlatformVersion);
-  let newBlocklistState = bs.getAddonBlocklistState(aNewAddon.id,
-                                                    aNewAddon.version);
+  let newBlocklistState = bs.getAddonBlocklistState(createWrapper(aNewAddon));
 
   // If the blocklist state hasn't changed then the properties don't need to
   // change
   if (newBlocklistState == oldBlocklistState)
     return;
 
   if (newBlocklistState == Ci.nsIBlocklistService.STATE_SOFTBLOCKED) {
     if (aNewAddon.type != "theme") {
@@ -6196,29 +6194,29 @@ AddonInternal.prototype = {
 
   get blocklistState() {
     let staticItem = findMatchingStaticBlocklistItem(this);
     if (staticItem)
       return staticItem.level;
 
     let bs = Cc["@mozilla.org/extensions/blocklist;1"].
              getService(Ci.nsIBlocklistService);
-    return bs.getAddonBlocklistState(this.id, this.version);
+    return bs.getAddonBlocklistState(createWrapper(this));
   },
 
   get blocklistURL() {
     let staticItem = findMatchingStaticBlocklistItem(this);
     if (staticItem) {
       let url = Services.urlFormatter.formatURLPref("extensions.blocklist.itemURL");
       return url.replace(/%blockID%/g, staticItem.blockID);
     }
 
     let bs = Cc["@mozilla.org/extensions/blocklist;1"].
              getService(Ci.nsIBlocklistService);
-    return bs.getAddonBlocklistURL(this.id, this.version);
+    return bs.getAddonBlocklistURL(createWrapper(this));
   },
 
   applyCompatibilityUpdate: function AddonInternal_applyCompatibilityUpdate(aUpdate, aSyncCompatibility) {
     this.targetApplications.forEach(function(aTargetApp) {
       aUpdate.targetApplications.forEach(function(aUpdateTarget) {
         if (aTargetApp.id == aUpdateTarget.id && (aSyncCompatibility ||
             Services.vc.compare(aTargetApp.maxVersion, aUpdateTarget.maxVersion) < 0)) {
           aTargetApp.minVersion = aUpdateTarget.minVersion;
@@ -6326,17 +6324,17 @@ function AddonWrapper(aAddon) {
     }
 
     return [objValue, false];
   }
 
   ["id", "syncGUID", "version", "type", "isCompatible", "isPlatformCompatible",
    "providesUpdatesSecurely", "blocklistState", "blocklistURL", "appDisabled",
    "softDisabled", "skinnable", "size", "foreignInstall", "hasBinaryComponents",
-   "strictCompatibility", "compatibilityOverrides"].forEach(function(aProp) {
+   "strictCompatibility", "compatibilityOverrides", "updateURL"].forEach(function(aProp) {
      this.__defineGetter__(aProp, function AddonWrapper_propertyGetter() aAddon[aProp]);
   }, this);
 
   ["fullDescription", "developerComments", "eula", "supportURL",
    "contributionURL", "contributionAmount", "averageRating", "reviewCount",
    "reviewURL", "totalDownloads", "weeklyDownloads", "dailyUsers",
    "repositoryStatus"].forEach(function(aProp) {
     this.__defineGetter__(aProp, function AddonWrapper_repoPropertyGetter() {
--- a/toolkit/mozapps/extensions/nsBlocklistService.js
+++ b/toolkit/mozapps/extensions/nsBlocklistService.js
@@ -44,16 +44,18 @@ const URI_BLOCKLIST_DIALOG            = 
 const DEFAULT_SEVERITY                = 3;
 const DEFAULT_LEVEL                   = 2;
 const MAX_BLOCK_LEVEL                 = 3;
 const SEVERITY_OUTDATED               = 0;
 const VULNERABILITYSTATUS_NONE             = 0;
 const VULNERABILITYSTATUS_UPDATE_AVAILABLE = 1;
 const VULNERABILITYSTATUS_NO_UPDATE        = 2;
 
+const EXTENSION_BLOCK_FILTERS = ["id", "name", "creator", "homepageURL", "updateURL"];
+
 var gLoggingEnabled = null;
 var gBlocklistEnabled = true;
 var gBlocklistLevel = DEFAULT_LEVEL;
 
 XPCOMUtils.defineLazyServiceGetter(this, "gConsole",
                                    "@mozilla.org/consoleservice;1",
                                    "nsIConsoleService");
 
@@ -312,26 +314,26 @@ Blocklist.prototype = {
           this._blocklistUpdated(null, null);
           break;
       }
       break;
     }
   },
 
   /* See nsIBlocklistService */
-  isAddonBlocklisted: function Blocklist_isAddonBlocklisted(id, version, appVersion, toolkitVersion) {
-    return this.getAddonBlocklistState(id, version, appVersion, toolkitVersion) ==
+  isAddonBlocklisted: function Blocklist_isAddonBlocklisted(addon, appVersion, toolkitVersion) {
+    return this.getAddonBlocklistState(addon, appVersion, toolkitVersion) ==
                    Ci.nsIBlocklistService.STATE_BLOCKED;
   },
 
   /* See nsIBlocklistService */
-  getAddonBlocklistState: function Blocklist_getAddonBlocklistState(id, version, appVersion, toolkitVersion) {
+  getAddonBlocklistState: function Blocklist_getAddonBlocklistState(addon, appVersion, toolkitVersion) {
     if (!this._addonEntries)
       this._loadBlocklist();
-    return this._getAddonBlocklistState(id, version, this._addonEntries,
+    return this._getAddonBlocklistState(addon, this._addonEntries,
                                         appVersion, toolkitVersion);
   },
 
   /**
    * Private version of getAddonBlocklistState that allows the caller to pass in
    * the add-on blocklist entries to compare against.
    *
    * @param   id
@@ -344,71 +346,98 @@ Blocklist.prototype = {
    *          The application version to compare to, will use the current
    *          version if null.
    * @param   toolkitVersion
    *          The toolkit version to compare to, will use the current version if
    *          null.
    * @returns The blocklist state for the item, one of the STATE constants as
    *          defined in nsIBlocklistService.
    */
-  _getAddonBlocklistState: function Blocklist_getAddonBlocklistStateCall(id,
-                           version, addonEntries, appVersion, toolkitVersion) {
+  _getAddonBlocklistState: function Blocklist_getAddonBlocklistStateCall(addon,
+                           addonEntries, appVersion, toolkitVersion) {
     if (!gBlocklistEnabled)
       return Ci.nsIBlocklistService.STATE_NOT_BLOCKED;
 
     if (!appVersion)
       appVersion = gApp.version;
     if (!toolkitVersion)
       toolkitVersion = gApp.platformVersion;
 
-    var blItem = this._findMatchingAddonEntry(addonEntries, id);
+    var blItem = this._findMatchingAddonEntry(addonEntries, addon);
     if (!blItem)
       return Ci.nsIBlocklistService.STATE_NOT_BLOCKED;
 
     for (let currentblItem of blItem.versions) {
-      if (currentblItem.includesItem(version, appVersion, toolkitVersion))
+      if (currentblItem.includesItem(addon.version, appVersion, toolkitVersion))
         return currentblItem.severity >= gBlocklistLevel ? Ci.nsIBlocklistService.STATE_BLOCKED :
                                                        Ci.nsIBlocklistService.STATE_SOFTBLOCKED;
     }
     return Ci.nsIBlocklistService.STATE_NOT_BLOCKED;
   },
 
   /**
    * Returns the set of prefs of the add-on stored in the blocklist file
    * (probably to revert them on disabling).
-   * @param id
-   *        ID of the add-on.
+   * @param addon
+   *        The add-on whose to-be-reset prefs are to be found.
    */
-  _getAddonPrefs: function Blocklist_getAddonPrefs(id) {
-    let entry = this._findMatchingAddonEntry(this._addonEntries, id);
+  _getAddonPrefs: function Blocklist_getAddonPrefs(addon) {
+    let entry = this._findMatchingAddonEntry(this._addonEntries, addon);
     return entry.prefs.slice(0);
   },
 
   _findMatchingAddonEntry: function Blocklist_findMatchingAddonEntry(aAddonEntries,
-                                                                     aId) {
+                                                                     aAddon) {
+    if (!aAddon)
+      return null;
+    // Returns true if the params object passes the constraints set by entry.
+    // (For every non-null property in entry, the same key must exist in
+    // params and value must be the same)
+    function checkEntry(entry, params) {
+      for (let [key, value] of entry) {
+        if (value === null || value === undefined)
+          continue;
+        if (params[key]) {
+          if (value instanceof RegExp) {
+            if (!value.test(params[key])) {
+              return false;
+            }
+          } else if (value !== params[key]) {
+            return false;
+          }
+        } else {
+          return false;
+        }
+      }
+      return true;
+    }
+
+    let params = {};
+    for (let filter of EXTENSION_BLOCK_FILTERS) {
+      params[filter] = aAddon[filter];
+    }
+    if (params.creator)
+      params.creator = params.creator.name;
     for (let entry of aAddonEntries) {
-      if (entry.id instanceof RegExp) {
-        if (entry.id.test(aId))
-          return entry;
-      } else if (entry.id == aId) {
-        return entry;
-      }
-    }
-    return null;
+      if (checkEntry(entry.attributes, params)) {
+         return entry;
+       }
+     }
+     return null;
   },
 
   /* See nsIBlocklistService */
-  getAddonBlocklistURL: function Blocklist_getAddonBlocklistURL(id, version, appVersion, toolkitVersion) {
+  getAddonBlocklistURL: function Blocklist_getAddonBlocklistURL(addon, appVersion, toolkitVersion) {
     if (!gBlocklistEnabled)
       return "";
 
     if (!this._addonEntries)
       this._loadBlocklist();
 
-    let blItem = this._findMatchingAddonEntry(this._addonEntries, id);
+    let blItem = this._findMatchingAddonEntry(this._addonEntries, addon);
     if (!blItem || !blItem.blockID)
       return null;
 
     return this._createBlocklistURL(blItem.blockID);
   },
 
   _createBlocklistURL: function Blocklist_createBlocklistURL(id) {
     let url = Services.urlFormatter.formatURLPref(PREF_BLOCKLIST_ITEM_URL);
@@ -729,28 +758,36 @@ Blocklist.prototype = {
     return result;
   },
 
   _handleEmItemNode: function Blocklist_handleEmItemNode(blocklistElement, result) {
     if (!matchesOSABI(blocklistElement))
       return;
 
     let blockEntry = {
-      id: null,
       versions: [],
       prefs: [],
-      blockID: null
+      blockID: null,
+      attributes: new Map()
+      // Atleast one of EXTENSION_BLOCK_FILTERS must get added to attributes
     };
 
+    // Any filter starting with '/' is interpreted as a regex. So if an attribute
+    // starts with a '/' it must be checked via a regex.
+    function regExpCheck(attr) {
+      return attr.startsWith("/") ? parseRegExp(attr) : attr;
+    }
+
+    for (let filter of EXTENSION_BLOCK_FILTERS) {
+      let attr = blocklistElement.getAttribute(filter);
+      if (attr)
+        blockEntry.attributes.set(filter, regExpCheck(attr));
+    }
+
     var childNodes = blocklistElement.childNodes;
-    var id = blocklistElement.getAttribute("id");
-    // Add-on IDs cannot contain '/', so an ID starting with '/' must be a regex
-    if (id.startsWith("/"))
-      id = parseRegExp(id);
-    blockEntry.id = id;
 
     for (let x = 0; x < childNodes.length; x++) {
       var childElement = childNodes.item(x);
       if (!(childElement instanceof Ci.nsIDOMElement))
         continue;
       if (childElement.localName === "prefs") {
         let prefElements = childElement.childNodes;
         for (let i = 0; i < prefElements.length; i++) {
@@ -923,30 +960,29 @@ Blocklist.prototype = {
     }
     var self = this;
     const types = ["extension", "theme", "locale", "dictionary", "service"];
     AddonManager.getAddonsByTypes(types, function blocklistUpdated_getAddonsByTypes(addons) {
 
       for (let addon of addons) {
         let oldState = Ci.nsIBlocklistService.STATE_NOTBLOCKED;
         if (oldAddonEntries)
-          oldState = self._getAddonBlocklistState(addon.id, addon.version,
-                                                  oldAddonEntries);
-        let state = self.getAddonBlocklistState(addon.id, addon.version);
+          oldState = self._getAddonBlocklistState(addon, oldAddonEntries);
+        let state = self.getAddonBlocklistState(addon);
 
         LOG("Blocklist state for " + addon.id + " changed from " +
             oldState + " to " + state);
 
         // We don't want to re-warn about add-ons
         if (state == oldState)
           continue;
 
         if (state === Ci.nsIBlocklistService.STATE_BLOCKED) {
           // It's a hard block. We must reset certain preferences.
-          let prefs = self._getAddonPrefs(addon.id);
+          let prefs = self._getAddonPrefs(addon);
           resetPrefs(prefs);
          }
 
         // Ensure that softDisabled is false if the add-on is not soft blocked
         if (state != Ci.nsIBlocklistService.STATE_SOFTBLOCKED)
           addon.softDisabled = false;
 
         // Don't warn about add-ons becoming unblocked.
@@ -968,17 +1004,17 @@ Blocklist.prototype = {
 
         addonList.push({
           name: addon.name,
           version: addon.version,
           icon: addon.iconURL,
           disable: false,
           blocked: state == Ci.nsIBlocklistService.STATE_BLOCKED,
           item: addon,
-          url: self.getAddonBlocklistURL(addon.id),
+          url: self.getAddonBlocklistURL(addon),
         });
       }
 
       AddonManagerPrivate.updateAddonAppDisabledStates();
 
       var phs = Cc["@mozilla.org/plugin/host;1"].
                 getService(Ci.nsIPluginHost);
       var plugins = phs.getPluginTags();
@@ -1051,17 +1087,17 @@ Blocklist.prototype = {
             continue;
 
           if (addon.item instanceof Ci.nsIPluginTag)
             addon.item.enabledState = Ci.nsIPluginTag.STATE_DISABLED;
           else {
             // This add-on is softblocked.
             addon.item.softDisabled = true;
             // We must revert certain prefs.
-            let prefs = self._getAddonPrefs(addon.item.id);
+            let prefs = self._getAddonPrefs(addon.item);
             resetPrefs(prefs);
           }
         }
 
         if (args.restart)
           restartApp();
 
         Services.obs.notifyObservers(self, "blocklist-updated", "");
new file mode 100644
--- /dev/null
+++ b/toolkit/mozapps/extensions/test/xpcshell/data/test_blocklist_metadata_filters_1.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="UTF-8"?>
+
+<blocklist xmlns="http://www.mozilla.org/2006/addons-blocklist">
+  <emItems>
+    <emItem name="/^Mozilla Corp\.$/">
+      <versionRange severity="1">
+        <targetApplication id="xpcshell@tests.mozilla.org">
+          <versionRange minVersion="1" maxVersion="2.*"/>
+        </targetApplication>
+      </versionRange>
+    </emItem>
+    <emItem id="/block2/" name="/^Moz/" creator="Dangerous"
+      homepageURL="/\.dangerous\.com/" updateURL="/\.dangerous\.com/">
+      <versionRange severity="3">
+        <targetApplication id="xpcshell@tests.mozilla.org">
+          <versionRange minVersion="1" maxVersion="2.*"/>
+        </targetApplication>
+      </versionRange>
+    </emItem>
+  </emItems>
+</blocklist>
--- a/toolkit/mozapps/extensions/test/xpcshell/data/test_bug393285.xml
+++ b/toolkit/mozapps/extensions/test/xpcshell/data/test_bug393285.xml
@@ -1,14 +1,17 @@
 <?xml version="1.0" encoding="UTF-8"?>
 
 <blocklist xmlns="http://www.mozilla.org/2006/addons-blocklist">
   <emItems>
     <emItem id="test_bug393285_2@tests.mozilla.org"/>
-    <emItem id="test_bug393285_3@tests.mozilla.org">
+    <emItem id="test_bug393285_3a@tests.mozilla.org">
+      <versionRange minVersion="1.0" maxVersion="1.0"/>
+    </emItem>
+    <emItem id="test_bug393285_3b@tests.mozilla.org">
       <versionRange minVersion="1.0" maxVersion="1.0"/>
     </emItem>
     <emItem id="test_bug393285_4@tests.mozilla.org">
       <versionRange minVersion="1.0" maxVersion="1.0">
         <targetApplication id="xpcshell@tests.mozilla.org">
           <versionRange minVersion="1.0" maxVersion="1.0"/>
         </targetApplication>
       </versionRange>
new file mode 100644
--- /dev/null
+++ b/toolkit/mozapps/extensions/test/xpcshell/test_blocklist_metadata_filters.js
@@ -0,0 +1,159 @@
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/
+ */
+
+// Tests blocking of extensions by ID, name, creator, homepageURL, updateURL
+// and RegExps for each. See bug 897735.
+
+const {classes: Cc, interfaces: Ci, utils: Cu, results: Cr} = Components;
+
+const URI_EXTENSION_BLOCKLIST_DIALOG = "chrome://mozapps/content/extensions/blocklist.xul";
+
+Cu.import("resource://testing-common/httpd.js");
+var testserver = new HttpServer();
+testserver.start(-1);
+gPort = testserver.identity.primaryPort;
+
+// register static files with server and interpolate port numbers in them
+mapFile("/data/test_blocklist_metadata_filters_1.xml", testserver);
+
+const profileDir = gProfD.clone();
+profileDir.append("extensions");
+
+// Don't need the full interface, attempts to call other methods will just
+// throw which is just fine
+var WindowWatcher = {
+  openWindow: function(parent, url, name, features, arguments) {
+    // Should be called to list the newly blocklisted items
+    do_check_eq(url, URI_EXTENSION_BLOCKLIST_DIALOG);
+
+    // Simulate auto-disabling any softblocks
+    var list = arguments.wrappedJSObject.list;
+    list.forEach(function(aItem) {
+      if (!aItem.blocked)
+        aItem.disable = true;
+    });
+
+    //run the code after the blocklist is closed
+    Services.obs.notifyObservers(null, "addon-blocklist-closed", null);
+
+  },
+
+  QueryInterface: function(iid) {
+    if (iid.equals(Ci.nsIWindowWatcher)
+     || iid.equals(Ci.nsISupports))
+      return this;
+
+    throw Cr.NS_ERROR_NO_INTERFACE;
+  }
+};
+
+var WindowWatcherFactory = {
+  createInstance: function createInstance(outer, iid) {
+    if (outer != null)
+      throw Cr.NS_ERROR_NO_AGGREGATION;
+    return WindowWatcher.QueryInterface(iid);
+  }
+};
+
+var registrar = Components.manager.QueryInterface(Ci.nsIComponentRegistrar);
+registrar.registerFactory(Components.ID("{1dfeb90a-2193-45d5-9cb8-864928b2af55}"),
+                          "Fake Window Watcher",
+                          "@mozilla.org/embedcomp/window-watcher;1",
+                          WindowWatcherFactory);
+
+
+function load_blocklist(aFile, aCallback) {
+  Services.obs.addObserver(function() {
+    Services.obs.removeObserver(arguments.callee, "blocklist-updated");
+
+    do_execute_soon(aCallback);
+  }, "blocklist-updated", false);
+
+  Services.prefs.setCharPref("extensions.blocklist.url", "http://localhost:" +
+                             gPort + "/data/" + aFile);
+  var blocklist = Cc["@mozilla.org/extensions/blocklist;1"].
+                  getService(Ci.nsITimerCallback);
+  blocklist.notify(null);
+}
+
+
+function end_test() {
+  testserver.stop(do_test_finished);
+}
+
+function run_test() {
+  do_test_pending();
+
+  createAppInfo("xpcshell@tests.mozilla.org", "XPCShell", "1", "1");
+
+  // Should get blocked by name
+  writeInstallRDFForExtension({
+    id: "block1@tests.mozilla.org",
+    version: "1.0",
+    name: "Mozilla Corp.",
+    targetApplications: [{
+      id: "xpcshell@tests.mozilla.org",
+      minVersion: "1",
+      maxVersion: "3"
+    }]
+  }, profileDir);
+
+  // Should get blocked by all the attributes.
+  writeInstallRDFForExtension({
+    id: "block2@tests.mozilla.org",
+    version: "1.0",
+    name: "Moz-addon",
+    creator: "Dangerous",
+    homepageURL: "www.extension.dangerous.com",
+    updateURL: "www.extension.dangerous.com/update.rdf",
+    targetApplications: [{
+      id: "xpcshell@tests.mozilla.org",
+      minVersion: "1",
+      maxVersion: "3"
+    }]
+  }, profileDir);
+
+  // Fails to get blocked because of a different ID even though other
+  // attributes match against a blocklist entry.
+  writeInstallRDFForExtension({
+    id: "block3@tests.mozilla.org",
+    version: "1.0",
+    name: "Moz-addon",
+    creator: "Dangerous",
+    homepageURL: "www.extensions.dangerous.com",
+    updateURL: "www.extension.dangerous.com/update.rdf",
+    targetApplications: [{
+      id: "xpcshell@tests.mozilla.org",
+      minVersion: "1",
+      maxVersion: "3"
+    }]
+  }, profileDir);
+
+  startupManager();
+
+  AddonManager.getAddonsByIDs(["block1@tests.mozilla.org",
+                               "block2@tests.mozilla.org",
+                               "block3@tests.mozilla.org"], function([a1, a2, a3]) {
+    do_check_eq(a1.blocklistState, Ci.nsIBlocklistService.STATE_NOT_BLOCKED);
+    do_check_eq(a2.blocklistState, Ci.nsIBlocklistService.STATE_NOT_BLOCKED);
+    do_check_eq(a3.blocklistState, Ci.nsIBlocklistService.STATE_NOT_BLOCKED);
+
+    run_test_1();
+  });
+}
+
+function run_test_1() {
+  load_blocklist("test_blocklist_metadata_filters_1.xml", function() {
+    restartManager();
+
+    AddonManager.getAddonsByIDs(["block1@tests.mozilla.org",
+                                 "block2@tests.mozilla.org",
+                                 "block3@tests.mozilla.org"], function([a1, a2, a3]) {
+      do_check_eq(a1.blocklistState, Ci.nsIBlocklistService.STATE_SOFTBLOCKED);
+      do_check_eq(a2.blocklistState, Ci.nsIBlocklistService.STATE_BLOCKED);
+      do_check_eq(a3.blocklistState, Ci.nsIBlocklistService.STATE_NOT_BLOCKED);
+      end_test();
+    });
+  });
+}
--- a/toolkit/mozapps/extensions/test/xpcshell/test_bug335238.js
+++ b/toolkit/mozapps/extensions/test/xpcshell/test_bug335238.js
@@ -74,30 +74,30 @@ var ADDONS = [
   {id: "bug335238_3@tests.mozilla.org",
    addon: "test_bug335238_3"},
   {id: "bug335238_4@tests.mozilla.org",
    addon: "test_bug335238_4"}
 ];
 
 // This is a replacement for the blocklist service
 var BlocklistService = {
-  getAddonBlocklistState: function(aId, aVersion, aAppVersion, aToolkitVersion) {
-    if (aId == "bug335238_3@tests.mozilla.org")
+  getAddonBlocklistState: function(aAddon, aAppVersion, aToolkitVersion) {
+    if (aAddon.id == "bug335238_3@tests.mozilla.org")
       return Ci.nsIBlocklistService.STATE_SOFTBLOCKED;
-    if (aId == "bug335238_4@tests.mozilla.org")
+    if (aAddon.id == "bug335238_4@tests.mozilla.org")
       return Ci.nsIBlocklistService.STATE_BLOCKED;
     return Ci.nsIBlocklistService.STATE_NOT_BLOCKED;
   },
 
   getPluginBlocklistState: function(aPlugin, aVersion, aAppVersion, aToolkitVersion) {
     return Ci.nsIBlocklistService.STATE_NOT_BLOCKED;
   },
 
-  isAddonBlocklisted: function(aId, aVersion, aAppVersion, aToolkitVersion) {
-    return this.getAddonBlocklistState(aId, aVersion, aAppVersion, aToolkitVersion) ==
+  isAddonBlocklisted: function(aAddon, aAppVersion, aToolkitVersion) {
+    return this.getAddonBlocklistState(aAddon, aAppVersion, aToolkitVersion) ==
            Ci.nsIBlocklistService.STATE_BLOCKED;
   },
 
   QueryInterface: function(iid) {
     if (iid.equals(Ci.nsIBlocklistService)
      || iid.equals(Ci.nsISupports))
       return this;
 
--- a/toolkit/mozapps/extensions/test/xpcshell/test_bug393285.js
+++ b/toolkit/mozapps/extensions/test/xpcshell/test_bug393285.js
@@ -1,55 +1,327 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/.
  */
 
+const {classes: Cc, interfaces: Ci, utils: Cu, results: Cr} = Components;
+
+const URI_EXTENSION_BLOCKLIST_DIALOG = "chrome://mozapps/content/extensions/blocklist.xul";
+
+Cu.import("resource://testing-common/httpd.js");
+var testserver = new HttpServer();
+testserver.start(-1);
+gPort = testserver.identity.primaryPort;
+
+// register static files with server and interpolate port numbers in them
+mapFile("/data/test_bug393285.xml", testserver);
+
+const profileDir = gProfD.clone();
+profileDir.append("extensions");
+
+let addonIDs = ["test_bug393285_1@tests.mozilla.org",
+                "test_bug393285_2@tests.mozilla.org",
+                "test_bug393285_3a@tests.mozilla.org",
+                "test_bug393285_3b@tests.mozilla.org",
+                "test_bug393285_4@tests.mozilla.org",
+                "test_bug393285_5@tests.mozilla.org",
+                "test_bug393285_6@tests.mozilla.org",
+                "test_bug393285_7@tests.mozilla.org",
+                "test_bug393285_8@tests.mozilla.org",
+                "test_bug393285_9@tests.mozilla.org",
+                "test_bug393285_10@tests.mozilla.org",
+                "test_bug393285_11@tests.mozilla.org",
+                "test_bug393285_12@tests.mozilla.org",
+                "test_bug393285_13@tests.mozilla.org",
+                "test_bug393285_14@tests.mozilla.org"];
+
+// A window watcher to deal with the blocklist UI dialog.
+var WindowWatcher = {
+  openWindow: function(parent, url, name, features, arguments) {
+    // Should be called to list the newly blocklisted items
+    do_check_eq(url, URI_EXTENSION_BLOCKLIST_DIALOG);
+
+    // Simulate auto-disabling any softblocks
+    var list = arguments.wrappedJSObject.list;
+    list.forEach(function(aItem) {
+      if (!aItem.blocked)
+        aItem.disable = true;
+    });
+
+    //run the code after the blocklist is closed
+    Services.obs.notifyObservers(null, "addon-blocklist-closed", null);
+
+  },
+
+  QueryInterface: function(iid) {
+    if (iid.equals(Ci.nsIWindowWatcher)
+     || iid.equals(Ci.nsISupports))
+      return this;
+
+    throw Cr.NS_ERROR_NO_INTERFACE;
+  }
+};
+
+var WindowWatcherFactory = {
+  createInstance: function createInstance(outer, iid) {
+    if (outer != null)
+      throw Cr.NS_ERROR_NO_AGGREGATION;
+    return WindowWatcher.QueryInterface(iid);
+  }
+};
+
+var registrar = Components.manager.QueryInterface(Ci.nsIComponentRegistrar);
+registrar.registerFactory(Components.ID("{1dfeb90a-2193-45d5-9cb8-864928b2af55}"),
+                          "Fake Window Watcher",
+                          "@mozilla.org/embedcomp/window-watcher;1",
+                          WindowWatcherFactory);
+
+
+function load_blocklist(aFile, aCallback) {
+  Services.obs.addObserver(function() {
+    Services.obs.removeObserver(arguments.callee, "blocklist-updated");
+
+    do_execute_soon(aCallback);
+  }, "blocklist-updated", false);
+
+  Services.prefs.setCharPref("extensions.blocklist.url", "http://localhost:" +
+                             gPort + "/data/" + aFile);
+  var blocklist = Cc["@mozilla.org/extensions/blocklist;1"].
+                  getService(Ci.nsITimerCallback);
+  blocklist.notify(null);
+}
+
+
+function end_test() {
+  testserver.stop(do_test_finished);
+}
+
 function run_test() {
+  do_test_pending();
+
   createAppInfo("xpcshell@tests.mozilla.org", "XPCShell", "1", "1.9");
 
-  // We cannot force the blocklist to update so just copy our test list to the profile
-  var blocklistFile = gProfD.clone();
-  blocklistFile.append("blocklist.xml");
-  if (blocklistFile.exists())
-    blocklistFile.remove(false);
-  var source = do_get_file("data/test_bug393285.xml");
-  source.copyTo(gProfD, "blocklist.xml");
+  writeInstallRDFForExtension({
+    id: "test_bug393285_1@tests.mozilla.org",
+    name: "extension 1",
+    version: "1.0",
+    targetApplications: [{
+      id: "xpcshell@tests.mozilla.org",
+      minVersion: "1",
+      maxVersion: "3"
+    }]
+  }, profileDir);
+
+
+  writeInstallRDFForExtension({
+    id: "test_bug393285_2@tests.mozilla.org",
+    name: "extension 2",
+    version: "1.0",
+    targetApplications: [{
+      id: "xpcshell@tests.mozilla.org",
+      minVersion: "1",
+      maxVersion: "3"
+    }]
+  }, profileDir);
+
+  writeInstallRDFForExtension({
+    id: "test_bug393285_3a@tests.mozilla.org",
+    name: "extension 3a",
+    version: "1.0",
+    targetApplications: [{
+      id: "xpcshell@tests.mozilla.org",
+      minVersion: "1",
+      maxVersion: "3"
+    }]
+  }, profileDir);
+
+  writeInstallRDFForExtension({
+    id: "test_bug393285_3b@tests.mozilla.org",
+    name: "extension 3b",
+    version: "2.0",
+    targetApplications: [{
+      id: "xpcshell@tests.mozilla.org",
+      minVersion: "1",
+      maxVersion: "3"
+    }]
+  }, profileDir);
 
-  var blocklist = Components.classes["@mozilla.org/extensions/blocklist;1"]
-                            .getService(Components.interfaces.nsIBlocklistService);
-  
-  // No info in blocklist, shouldn't be blocked
-  do_check_false(blocklist.isAddonBlocklisted("test_bug393285_1@tests.mozilla.org", "1", "1", "1.9"));
+  writeInstallRDFForExtension({
+    id: "test_bug393285_4@tests.mozilla.org",
+    name: "extension 4",
+    version: "1.0",
+    targetApplications: [{
+      id: "xpcshell@tests.mozilla.org",
+      minVersion: "1",
+      maxVersion: "3"
+    }]
+  }, profileDir);
+
+  writeInstallRDFForExtension({
+    id: "test_bug393285_5@tests.mozilla.org",
+    name: "extension 5",
+    version: "1.0",
+    targetApplications: [{
+      id: "xpcshell@tests.mozilla.org",
+      minVersion: "1",
+      maxVersion: "3"
+    }]
+  }, profileDir);
+
+  writeInstallRDFForExtension({
+    id: "test_bug393285_6@tests.mozilla.org",
+    name: "extension 6",
+    version: "1.0",
+    targetApplications: [{
+      id: "xpcshell@tests.mozilla.org",
+      minVersion: "1",
+      maxVersion: "3"
+    }]
+  }, profileDir);
 
-  // Should always be blocked
-  do_check_true(blocklist.isAddonBlocklisted("test_bug393285_2@tests.mozilla.org", "1", "1", "1.9"));
+  writeInstallRDFForExtension({
+    id: "test_bug393285_7@tests.mozilla.org",
+    name: "extension 7",
+    version: "1.0",
+    targetApplications: [{
+      id: "xpcshell@tests.mozilla.org",
+      minVersion: "1",
+      maxVersion: "3"
+    }]
+  }, profileDir);
 
-  // Only version 1 should be blocked
-  do_check_true(blocklist.isAddonBlocklisted("test_bug393285_3@tests.mozilla.org", "1", "1", "1.9"));
-  do_check_false(blocklist.isAddonBlocklisted("test_bug393285_3@tests.mozilla.org", "2", "1", "1.9"));
+  writeInstallRDFForExtension({
+    id: "test_bug393285_8@tests.mozilla.org",
+    name: "extension 8",
+    version: "1.0",
+    targetApplications: [{
+      id: "xpcshell@tests.mozilla.org",
+      minVersion: "1",
+      maxVersion: "3"
+    }]
+  }, profileDir);
+
+  writeInstallRDFForExtension({
+    id: "test_bug393285_9@tests.mozilla.org",
+    name: "extension 9",
+    version: "1.0",
+    targetApplications: [{
+      id: "xpcshell@tests.mozilla.org",
+      minVersion: "1",
+      maxVersion: "3"
+    }]
+  }, profileDir);
 
-  // Should be blocked for app version 1
-  do_check_true(blocklist.isAddonBlocklisted("test_bug393285_4@tests.mozilla.org", "1", "1", "1.9"));
-  do_check_false(blocklist.isAddonBlocklisted("test_bug393285_4@tests.mozilla.org", "1", "2", "1.9"));
+  writeInstallRDFForExtension({
+    id: "test_bug393285_10@tests.mozilla.org",
+    name: "extension 10",
+    version: "1.0",
+    targetApplications: [{
+      id: "xpcshell@tests.mozilla.org",
+      minVersion: "1",
+      maxVersion: "3"
+    }]
+  }, profileDir);
+
+  writeInstallRDFForExtension({
+    id: "test_bug393285_11@tests.mozilla.org",
+    name: "extension 11",
+    version: "1.0",
+    targetApplications: [{
+      id: "xpcshell@tests.mozilla.org",
+      minVersion: "1",
+      maxVersion: "3"
+    }]
+  }, profileDir);
 
-  // Not blocklisted because we are a different OS
-  do_check_false(blocklist.isAddonBlocklisted("test_bug393285_5@tests.mozilla.org", "1", "2", "1.9"));
+  writeInstallRDFForExtension({
+    id: "test_bug393285_12@tests.mozilla.org",
+    name: "extension 12",
+    version: "1.0",
+    targetApplications: [{
+      id: "xpcshell@tests.mozilla.org",
+      minVersion: "1",
+      maxVersion: "3"
+    }]
+  }, profileDir);
 
-  // Blocklisted based on OS
-  do_check_true(blocklist.isAddonBlocklisted("test_bug393285_6@tests.mozilla.org", "1", "2", "1.9"));
-  do_check_true(blocklist.isAddonBlocklisted("test_bug393285_7@tests.mozilla.org", "1", "2", "1.9"));
+  writeInstallRDFForExtension({
+    id: "test_bug393285_13@tests.mozilla.org",
+    name: "extension 13",
+    version: "1.0",
+    targetApplications: [{
+      id: "xpcshell@tests.mozilla.org",
+      minVersion: "1",
+      maxVersion: "3"
+    }]
+  }, profileDir);
+
+  writeInstallRDFForExtension({
+    id: "test_bug393285_14@tests.mozilla.org",
+    name: "extension 14",
+    version: "1.0",
+    targetApplications: [{
+      id: "xpcshell@tests.mozilla.org",
+      minVersion: "1",
+      maxVersion: "3"
+    }]
+  }, profileDir);
+
+  startupManager();
 
-  // Not blocklisted because we are a different ABI
-  do_check_false(blocklist.isAddonBlocklisted("test_bug393285_8@tests.mozilla.org", "1", "2", "1.9"));
+  AddonManager.getAddonsByIDs(addonIDs, function(addons) {
+    for (addon of addons) {
+      do_check_eq(addon.blocklistState, Ci.nsIBlocklistService.STATE_NOT_BLOCKED);
+    }
+    run_test_1();
+  });
+}
+
+function run_test_1() {
+  load_blocklist("test_bug393285.xml", function() {
+    restartManager();
 
-  // Blocklisted based on ABI
-  do_check_true(blocklist.isAddonBlocklisted("test_bug393285_9@tests.mozilla.org", "1", "2", "1.9"));
-  do_check_true(blocklist.isAddonBlocklisted("test_bug393285_10@tests.mozilla.org", "1", "2", "1.9"));
+    var blocklist = Cc["@mozilla.org/extensions/blocklist;1"]
+                    .getService(Ci.nsIBlocklistService);
+
+    AddonManager.getAddonsByIDs(addonIDs,
+                               function([a1, a2, a3, a4, a5, a6, a7, a8, a9, a10,
+                                         a11, a12, a13, a14, a15]) {
+      // No info in blocklist, shouldn't be blocked
+      do_check_false(blocklist.isAddonBlocklisted(a1, "1", "1.9"));
+
+      // Should always be blocked
+      do_check_true(blocklist.isAddonBlocklisted(a2, "1", "1.9"));
+
+      // Only version 1 should be blocked
+      do_check_true(blocklist.isAddonBlocklisted(a3, "1", "1.9"));
+      do_check_false(blocklist.isAddonBlocklisted(a4, "1", "1.9"));
 
-  // Doesnt match both os and abi so not blocked
-  do_check_false(blocklist.isAddonBlocklisted("test_bug393285_11@tests.mozilla.org", "1", "2", "1.9"));
-  do_check_false(blocklist.isAddonBlocklisted("test_bug393285_12@tests.mozilla.org", "1", "2", "1.9"));
-  do_check_false(blocklist.isAddonBlocklisted("test_bug393285_13@tests.mozilla.org", "1", "2", "1.9"));
+      // Should be blocked for app version 1
+      do_check_true(blocklist.isAddonBlocklisted(a5, "1", "1.9"));
+      do_check_false(blocklist.isAddonBlocklisted(a5, "2", "1.9"));
+
+      // Not blocklisted because we are a different OS
+      do_check_false(blocklist.isAddonBlocklisted(a6, "2", "1.9"));
+
+      // Blocklisted based on OS
+      do_check_true(blocklist.isAddonBlocklisted(a7, "2", "1.9"));
+      do_check_true(blocklist.isAddonBlocklisted(a8, "2", "1.9"));
+
+      // Not blocklisted because we are a different ABI
+      do_check_false(blocklist.isAddonBlocklisted(a9, "2", "1.9"));
 
-  // Matches both os and abi so blocked
-  do_check_true(blocklist.isAddonBlocklisted("test_bug393285_14@tests.mozilla.org", "1", "2", "1.9"));
+      // Blocklisted based on ABI
+      do_check_true(blocklist.isAddonBlocklisted(a10, "2", "1.9"));
+      do_check_true(blocklist.isAddonBlocklisted(a11, "2", "1.9"));
+
+      // Doesnt match both os and abi so not blocked
+      do_check_false(blocklist.isAddonBlocklisted(a12, "2", "1.9"));
+      do_check_false(blocklist.isAddonBlocklisted(a13, "2", "1.9"));
+      do_check_false(blocklist.isAddonBlocklisted(a14, "2", "1.9"));
+
+      // Matches both os and abi so blocked
+      do_check_true(blocklist.isAddonBlocklisted(a15, "2", "1.9"));
+      end_test();
+    });
+  });
 }
--- a/toolkit/mozapps/extensions/test/xpcshell/test_bug406118.js
+++ b/toolkit/mozapps/extensions/test/xpcshell/test_bug406118.js
@@ -1,26 +1,167 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/.
  */
 
+let addonIDs = ["test_bug393285_1@tests.mozilla.org",
+                "test_bug393285_2@tests.mozilla.org",
+                "test_bug393285_3a@tests.mozilla.org",
+                "test_bug393285_4@tests.mozilla.org"];
+
+const {classes: Cc, interfaces: Ci, utils: Cu, results: Cr} = Components;
+
+const URI_EXTENSION_BLOCKLIST_DIALOG = "chrome://mozapps/content/extensions/blocklist.xul";
+
+Cu.import("resource://testing-common/httpd.js");
+var testserver = new HttpServer();
+testserver.start(-1);
+gPort = testserver.identity.primaryPort;
+
+// register static files with server and interpolate port numbers in them
+mapFile("/data/test_bug393285.xml", testserver);
+
+const profileDir = gProfD.clone();
+profileDir.append("extensions");
+
+// A window watcher to deal with the blocklist UI dialog.
+var WindowWatcher = {
+  openWindow: function(parent, url, name, features, arguments) {
+    // Should be called to list the newly blocklisted items
+    do_check_eq(url, URI_EXTENSION_BLOCKLIST_DIALOG);
+
+    // Simulate auto-disabling any softblocks
+    var list = arguments.wrappedJSObject.list;
+    list.forEach(function(aItem) {
+      if (!aItem.blocked)
+        aItem.disable = true;
+    });
+
+    //run the code after the blocklist is closed
+    Services.obs.notifyObservers(null, "addon-blocklist-closed", null);
+
+  },
+
+  QueryInterface: function(iid) {
+    if (iid.equals(Ci.nsIWindowWatcher)
+     || iid.equals(Ci.nsISupports))
+      return this;
+
+    throw Cr.NS_ERROR_NO_INTERFACE;
+  }
+};
+
+var WindowWatcherFactory = {
+  createInstance: function createInstance(outer, iid) {
+    if (outer != null)
+      throw Cr.NS_ERROR_NO_AGGREGATION;
+    return WindowWatcher.QueryInterface(iid);
+  }
+};
+
+var registrar = Components.manager.QueryInterface(Ci.nsIComponentRegistrar);
+registrar.registerFactory(Components.ID("{1dfeb90a-2193-45d5-9cb8-864928b2af55}"),
+                          "Fake Window Watcher",
+                          "@mozilla.org/embedcomp/window-watcher;1",
+                          WindowWatcherFactory);
+
+
+function load_blocklist(aFile, aCallback) {
+  Services.obs.addObserver(function() {
+    Services.obs.removeObserver(arguments.callee, "blocklist-updated");
+
+    do_execute_soon(aCallback);
+  }, "blocklist-updated", false);
+
+  Services.prefs.setCharPref("extensions.blocklist.url", "http://localhost:" +
+                             gPort + "/data/" + aFile);
+  var blocklist = Cc["@mozilla.org/extensions/blocklist;1"].
+                  getService(Ci.nsITimerCallback);
+  blocklist.notify(null);
+}
+
+
+function end_test() {
+  testserver.stop(do_test_finished);
+}
+
 function run_test() {
+  do_test_pending();
+
   createAppInfo("xpcshell@tests.mozilla.org", "XPCShell", "1", "1.9");
 
-  // We cannot force the blocklist to update so just copy our test list to the profile
-  var blocklistFile = gProfD.clone();
-  blocklistFile.append("blocklist.xml");
-  if (blocklistFile.exists())
-    blocklistFile.remove(false);
-  var source = do_get_file("data/test_bug393285.xml");
-  source.copyTo(gProfD, "blocklist.xml");
+  writeInstallRDFForExtension({
+    id: "test_bug393285_1@tests.mozilla.org",
+    name: "extension 1",
+    version: "1.0",
+    targetApplications: [{
+      id: "xpcshell@tests.mozilla.org",
+      minVersion: "1",
+      maxVersion: "3"
+    }]
+  }, profileDir);
+
+
+  writeInstallRDFForExtension({
+    id: "test_bug393285_2@tests.mozilla.org",
+    name: "extension 2",
+    version: "1.0",
+    targetApplications: [{
+      id: "xpcshell@tests.mozilla.org",
+      minVersion: "1",
+      maxVersion: "3"
+    }]
+  }, profileDir);
+
+  writeInstallRDFForExtension({
+    id: "test_bug393285_3a@tests.mozilla.org",
+    name: "extension 3a",
+    version: "1.0",
+    targetApplications: [{
+      id: "xpcshell@tests.mozilla.org",
+      minVersion: "1",
+      maxVersion: "3"
+    }]
+  }, profileDir);
 
-  var blocklist = Components.classes["@mozilla.org/extensions/blocklist;1"]
-                            .getService(Components.interfaces.nsIBlocklistService);
-  
-  // All these should be blocklisted for the current app.
-  do_check_false(blocklist.isAddonBlocklisted("test_bug393285_1@tests.mozilla.org", "1", null, null));
-  do_check_true(blocklist.isAddonBlocklisted("test_bug393285_2@tests.mozilla.org", "1", null, null));
-  do_check_true(blocklist.isAddonBlocklisted("test_bug393285_3@tests.mozilla.org", "1", null, null));
-  do_check_true(blocklist.isAddonBlocklisted("test_bug393285_4@tests.mozilla.org", "1", null, null));
+  writeInstallRDFForExtension({
+    id: "test_bug393285_4@tests.mozilla.org",
+    name: "extension 4",
+    version: "1.0",
+    targetApplications: [{
+      id: "xpcshell@tests.mozilla.org",
+      minVersion: "1",
+      maxVersion: "3"
+    }]
+  }, profileDir);
+
+  startupManager();
+
+  AddonManager.getAddonsByIDs(addonIDs, function(addons) {
+    for (addon of addons) {
+      do_check_eq(addon.blocklistState, Ci.nsIBlocklistService.STATE_NOT_BLOCKED);
+    }
+    run_test_1();
+  });
+}
 
+function run_test_1() {
+  load_blocklist("test_bug393285.xml", function() {
+    restartManager();
+
+    var blocklist = Cc["@mozilla.org/extensions/blocklist;1"]
+                    .getService(Ci.nsIBlocklistService);
+
+    AddonManager.getAddonsByIDs(addonIDs,
+                               function([a1, a2, a3, a4]) {
+      // No info in blocklist, shouldn't be blocked
+      do_check_false(blocklist.isAddonBlocklisted(a1, null, null));
+
+      // All these should be blocklisted for the current app.
+      do_check_true(blocklist.isAddonBlocklisted(a2, null, null));
+      do_check_true(blocklist.isAddonBlocklisted(a3, null, null));
+      do_check_true(blocklist.isAddonBlocklisted(a4, null, null));
+
+      end_test();
+    });
+  });
 }
--- a/toolkit/mozapps/extensions/test/xpcshell/test_bug455906.js
+++ b/toolkit/mozapps/extensions/test/xpcshell/test_bug455906.js
@@ -456,21 +456,21 @@ function check_test_pt3() {
     do_check_eq(check_plugin_state(PLUGINS[2]), "false,true");
     do_check_eq(check_plugin_state(PLUGINS[3]), "true,true");
     do_check_eq(check_plugin_state(PLUGINS[4]), "false,true");
 
     // Should have gained the blocklist state but no longer be soft disabled
     do_check_eq(check_addon_state(addons[3]), "false,false,true");
 
     // Check blockIDs are correct
-    do_check_eq(blocklist.getAddonBlocklistURL(addons[0].id,''),create_blocklistURL(addons[0].id));
-    do_check_eq(blocklist.getAddonBlocklistURL(addons[1].id,''),create_blocklistURL(addons[1].id));
-    do_check_eq(blocklist.getAddonBlocklistURL(addons[2].id,''),create_blocklistURL(addons[2].id));
-    do_check_eq(blocklist.getAddonBlocklistURL(addons[3].id,''),create_blocklistURL(addons[3].id));
-    do_check_eq(blocklist.getAddonBlocklistURL(addons[4].id,''),create_blocklistURL(addons[4].id));
+    do_check_eq(blocklist.getAddonBlocklistURL(addons[0]),create_blocklistURL(addons[0].id));
+    do_check_eq(blocklist.getAddonBlocklistURL(addons[1]),create_blocklistURL(addons[1].id));
+    do_check_eq(blocklist.getAddonBlocklistURL(addons[2]),create_blocklistURL(addons[2].id));
+    do_check_eq(blocklist.getAddonBlocklistURL(addons[3]),create_blocklistURL(addons[3].id));
+    do_check_eq(blocklist.getAddonBlocklistURL(addons[4]),create_blocklistURL(addons[4].id));
 
     // All plugins have the same blockID on the test
     do_check_eq(blocklist.getPluginBlocklistURL(PLUGINS[0]), create_blocklistURL('test_bug455906_plugin'));
     do_check_eq(blocklist.getPluginBlocklistURL(PLUGINS[1]), create_blocklistURL('test_bug455906_plugin'));
     do_check_eq(blocklist.getPluginBlocklistURL(PLUGINS[2]), create_blocklistURL('test_bug455906_plugin'));
     do_check_eq(blocklist.getPluginBlocklistURL(PLUGINS[3]), create_blocklistURL('test_bug455906_plugin'));
     do_check_eq(blocklist.getPluginBlocklistURL(PLUGINS[4]), create_blocklistURL('test_bug455906_plugin'));
 
--- a/toolkit/mozapps/extensions/test/xpcshell/xpcshell-shared.ini
+++ b/toolkit/mozapps/extensions/test/xpcshell/xpcshell-shared.ini
@@ -12,16 +12,17 @@ skip-if = os == "android"
 [test_DeferredSave.js]
 [test_LightweightThemeManager.js]
 [test_XPIcancel.js]
 [test_backgroundupdate.js]
 [test_bad_json.js]
 [test_badschema.js]
 [test_blocklistchange.js]
 [test_blocklist_prefs.js]
+[test_blocklist_metadata_filters.js]
 # Bug 676992: test consistently hangs on Android
 skip-if = os == "android"
 [test_blocklist_regexp.js]
 skip-if = os == "android"
 [test_bootstrap.js]
 # Bug 676992: test consistently hangs on Android
 skip-if = os == "android"
 [test_bug299716.js]
--- a/toolkit/mozapps/update/content/updates.js
+++ b/toolkit/mozapps/update/content/updates.js
@@ -864,17 +864,17 @@ var gIncompatibleCheckPage = {
   },
 
   onUpdateAvailable: function(addon, install) {
     // If the new version of this add-on is blocklisted for the new application
     // then it isn't a valid update and the user should still be warned that
     // the add-on will become incompatible.
     let bs = CoC["@mozilla.org/extensions/blocklist;1"].
              getService(CoI.nsIBlocklistService);
-    if (bs.isAddonBlocklisted(addon.id, install.version,
+    if (bs.isAddonBlocklisted(addon,
                               gUpdates.update.appVersion,
                               gUpdates.update.platformVersion))
       return;
 
     // Compatibility or new version updates mean the same thing here.
     this.onCompatibilityUpdateAvailable(addon);
   },
 
--- a/toolkit/mozapps/update/nsUpdateService.js
+++ b/toolkit/mozapps/update/nsUpdateService.js
@@ -2967,17 +2967,17 @@ UpdateService.prototype = {
     if (getPref("getIntPref", PREF_APP_UPDATE_INCOMPATIBLE_MODE, 0) == 1)
       return;
 
     // If the new version of this add-on is blocklisted for the new application
     // then it isn't a valid update and the user should still be warned that
     // the add-on will become incompatible.
     let bs = Cc["@mozilla.org/extensions/blocklist;1"].
              getService(Ci.nsIBlocklistService);
-    if (bs.isAddonBlocklisted(addon.id, install.version,
+    if (bs.isAddonBlocklisted(addon,
                               gUpdates.update.appVersion,
                               gUpdates.update.platformVersion))
       return;
 
     // Compatibility or new version updates mean the same thing here.
     this.onCompatibilityUpdateAvailable(addon);
   },
 
--- a/xpcom/system/nsIBlocklistService.idl
+++ b/xpcom/system/nsIBlocklistService.idl
@@ -24,52 +24,48 @@ interface nsIBlocklistService : nsISuppo
   const unsigned long STATE_OUTDATED    = 3;
   // Indicates that the item is vulnerable and there is an update.
   const unsigned long STATE_VULNERABLE_UPDATE_AVAILABLE = 4;
   // Indicates that the item is vulnerable and there is no update.
   const unsigned long STATE_VULNERABLE_NO_UPDATE = 5;
 
   /**
    * Determine if an item is blocklisted
-   * @param   id
-   *          The ID of the item.
-   * @param   version
-   *          The item's version.
+   * @param   addon
+   *          The addon item to be checked.
    * @param   appVersion
    *          The version of the application we are checking in the blocklist.
    *          If this parameter is null, 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 null, 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,
+  boolean isAddonBlocklisted(in jsval addon,
                              [optional] in AString appVersion,
                              [optional] in AString toolkitVersion);
 
   /**
    * Determine the blocklist state of an add-on
    * @param   id
-   *          The ID of the item.
-   * @param   version
-   *          The item's version.
+   *          The addon item to be checked.
    * @param   appVersion
    *          The version of the application we are checking in the blocklist.
    *          If this parameter is null, 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 null, the version of the running toolkit
    *          is used.
    * @returns The STATE constant.
    */
-  unsigned long getAddonBlocklistState(in AString id, in AString version,
+  unsigned long getAddonBlocklistState(in jsval addon,
                                        [optional] in AString appVersion,
                                        [optional] in AString toolkitVersion);
 
   /**
    * Determine the blocklist state of a plugin
    * @param   plugin
    *          The plugin to get the state for
    * @param   appVersion
@@ -83,21 +79,21 @@ interface nsIBlocklistService : nsISuppo
    * @returns The STATE constant.
    */
   unsigned long getPluginBlocklistState(in nsIPluginTag plugin,
                                         [optional] in AString appVersion,
                                         [optional] in AString toolkitVersion);
 
   /**
    * Determine the blocklist web page of an add-on.
-   * @param   id
-   *          The ID of the blocked add-on.
+   * @param   addon
+   *          The addon item whose url is required.
    * @returns The URL of the description page.
    */
-  AString getAddonBlocklistURL(in AString id, in AString version,
+  AString getAddonBlocklistURL(in jsval addon,
                               [optional] in AString appVersion,
                               [optional] in AString toolkitVersion);
 
   /**
    * Determine the blocklist web page of a plugin.
    * @param   plugin
    *          The blocked plugin that we are determining the web page for.
    * @returns The URL of the description page.