Merge mozilla-central and fx-team
authorEd Morley <emorley@mozilla.com>
Mon, 06 Jan 2014 15:20:45 +0000
changeset 162236 2068b0f801c9e3d680aeff54bddcaddebab13624
parent 162182 bafaf8d078d8e0c2f61973b54231ef23ac3c7571 (current diff)
parent 162235 70c67626470c213903c74718cf510b1bdcfe6d5e (diff)
child 162237 c467462fae8e0fb61578c19646f16f5998dc423f
push idunknown
push userunknown
push dateunknown
milestone29.0a1
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.