Bug 693899 - Support detecting binary components, and enable strict compatibility checking when found. r=dtownsend
authorBlair McBride <bmcbride@mozilla.com>
Tue, 01 Nov 2011 18:48:49 +1300
changeset 79988 6b7874f5b777f0d61c955cac2c4f5bb1c8974205
parent 79987 1fb831781a15926b4d4969621f18f0937170b28e
child 79989 41c52a9e1337163736f5e297478ee773567b7484
push idunknown
push userunknown
push dateunknown
reviewersdtownsend
bugs693899
milestone10.0a1
Bug 693899 - Support detecting binary components, and enable strict compatibility checking when found. r=dtownsend
toolkit/mozapps/extensions/ChromeManifestParser.jsm
toolkit/mozapps/extensions/Makefile.in
toolkit/mozapps/extensions/XPIProvider.jsm
toolkit/mozapps/extensions/test/addons/test_chromemanifest_1/chrome.manifest
toolkit/mozapps/extensions/test/addons/test_chromemanifest_1/install.rdf
toolkit/mozapps/extensions/test/addons/test_chromemanifest_2/chrome.manifest
toolkit/mozapps/extensions/test/addons/test_chromemanifest_2/install.rdf
toolkit/mozapps/extensions/test/addons/test_chromemanifest_3/chrome.manifest
toolkit/mozapps/extensions/test/addons/test_chromemanifest_3/inner.jar
toolkit/mozapps/extensions/test/addons/test_chromemanifest_3/install.rdf
toolkit/mozapps/extensions/test/addons/test_chromemanifest_4/chrome.manifest
toolkit/mozapps/extensions/test/addons/test_chromemanifest_4/components/components.manifest
toolkit/mozapps/extensions/test/addons/test_chromemanifest_4/components/other/something.manifest
toolkit/mozapps/extensions/test/addons/test_chromemanifest_4/install.rdf
toolkit/mozapps/extensions/test/addons/test_chromemanifest_5/chrome.manifest
toolkit/mozapps/extensions/test/addons/test_chromemanifest_5/install.rdf
toolkit/mozapps/extensions/test/addons/test_migrate8/chrome.manifest
toolkit/mozapps/extensions/test/addons/test_migrate8/install.rdf
toolkit/mozapps/extensions/test/xpcshell/test_ChromeManifestParser.js
toolkit/mozapps/extensions/test/xpcshell/test_hasbinarycomponents.js
toolkit/mozapps/extensions/test/xpcshell/test_migrate1.js
toolkit/mozapps/extensions/test/xpcshell/test_migrate4.js
toolkit/mozapps/extensions/test/xpcshell/xpcshell.ini
new file mode 100644
--- /dev/null
+++ b/toolkit/mozapps/extensions/ChromeManifestParser.jsm
@@ -0,0 +1,192 @@
+/* ***** BEGIN LICENSE BLOCK *****
+ * Version: MPL 1.1/GPL 2.0/LGPL 2.1
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is the Extension Manager.
+ *
+ * The Initial Developer of the Original Code is
+ * the Mozilla Foundation.
+ * Portions created by the Initial Developer are Copyright (C) 2011
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ *   Blair McBride <bmcbride@mozilla.com>
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * either the GNU General Public License Version 2 or later (the "GPL"), or
+ * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+ * in which case the provisions of the GPL or the LGPL are applicable instead
+ * of those above. If you wish to allow use of your version of this file only
+ * under the terms of either the GPL or the LGPL, and not to allow others to
+ * use your version of this file under the terms of the MPL, indicate your
+ * decision by deleting the provisions above and replace them with the notice
+ * and other provisions required by the GPL or the LGPL. If you do not delete
+ * the provisions above, a recipient may use your version of this file under
+ * the terms of any one of the MPL, the GPL or the LGPL.
+ *
+ * ***** END LICENSE BLOCK ***** */
+
+"use strict";
+
+var EXPORTED_SYMBOLS = ["ChromeManifestParser"];
+
+const Cc = Components.classes;
+const Ci = Components.interfaces;
+const Cr = Components.results;
+const Cu = Components.utils;
+
+Cu.import("resource://gre/modules/Services.jsm");
+Cu.import("resource://gre/modules/NetUtil.jsm");
+
+const MSG_JAR_FLUSH = "AddonJarFlush";
+
+
+/**
+ * Sends local and remote notifications to flush a JAR file cache entry
+ *
+ * @param aJarFile
+ *        The ZIP/XPI/JAR file as a nsIFile
+ */
+function flushJarCache(aJarFile) {
+  Services.obs.notifyObservers(aJarFile, "flush-cache-entry", null);
+  Cc["@mozilla.org/globalmessagemanager;1"].getService(Ci.nsIChromeFrameMessageManager)
+    .sendAsyncMessage(MSG_JAR_FLUSH, aJarFile.path);
+}
+
+
+/**
+ * Parses chrome manifest files.
+ */
+var ChromeManifestParser = {
+
+  /**
+   * Reads and parses a chrome manifest file located at a specified URI, and all
+   * secondary manifests it references.
+   *
+   * @param  aURI
+   *         A nsIURI pointing to a chrome manifest.
+   *         Typically a file: or jar: URI.
+   * @return Array of objects describing each manifest instruction, in the form:
+   *         { type: instruction-type, baseURI: string-uri, args: [arguments] }
+   **/
+  parseSync: function CMP_parseSync(aURI) {
+    function parseLine(aLine) {
+      let line = aLine.trim();
+      if (line.length == 0 || line.charAt(0) == '#')
+        return;
+      let tokens = line.split(/\s+/);
+      let type = tokens.shift();
+      if (type == "manifest") {
+        let uri = NetUtil.newURI(tokens.shift(), null, aURI);
+        data = data.concat(this.parseSync(uri));
+      } else {
+        data.push({type: type, baseURI: baseURI, args: tokens});
+      }
+    }
+
+    let contents = "";
+    try {
+      if (aURI.scheme == "jar")
+        contents = this._readFromJar(aURI);
+      else
+        contents = this._readFromFile(aURI);
+    } catch (e) {
+      // Silently fail.
+    }
+
+    if (!contents)
+      return [];
+
+    let baseURI = NetUtil.newURI(".", null, aURI).spec;
+
+    let data = [];
+    let lines = contents.split("\n");
+    lines.forEach(parseLine.bind(this));
+    return data;
+  },
+  
+  _readFromJar: function CMP_readFromJar(aURI) {
+    let data = "";
+    let entries = [];
+    let readers = [];
+    
+    try {
+      // Deconstrict URI, which can be nested jar: URIs. 
+      let uri = aURI.clone();
+      while (uri instanceof Ci.nsIJARURI) {
+        entries.push(uri.JAREntry);
+        uri = uri.JARFile;
+      }
+
+      // Open the base jar.
+      let reader = Cc["@mozilla.org/libjar/zip-reader;1"].
+                   createInstance(Ci.nsIZipReader);
+      reader.open(uri.QueryInterface(Ci.nsIFileURL).file);
+      readers.push(reader);
+  
+      // Open the nested jars.
+      for (let i = entries.length - 1; i > 0; i--) {
+        let innerReader = Cc["@mozilla.org/libjar/zip-reader;1"].
+                          createInstance(Ci.nsIZipReader);
+        innerReader.openInner(reader, entries[i]);
+        readers.push(innerReader);
+        reader = innerReader;
+      }
+      
+      // First entry is the actual file we want to read.
+      let zis = reader.getInputStream(entries[0]);
+      data = NetUtil.readInputStreamToString(zis, zis.available());
+    }
+    finally {
+      // Close readers in reverse order.
+      for (let i = readers.length - 1; i >= 0; i--) {
+        readers[i].close();
+        flushJarCache(readers[i].file);
+      }
+    }
+    
+    return data;
+  },
+  
+  _readFromFile: function CMP_readFromFile(aURI) {
+    let file = aURI.QueryInterface(Ci.nsIFileURL).file;
+    if (!file.exists() || !file.isFile())
+      return "";
+    
+    let data = "";
+    let fis = Cc["@mozilla.org/network/file-input-stream;1"].
+              createInstance(Ci.nsIFileInputStream);
+    try {
+      fis.init(file, -1, -1, false);
+      data = NetUtil.readInputStreamToString(fis, fis.available());
+    } finally {
+      fis.close();
+    }
+    return data;
+  },
+
+  /**
+  * Detects if there were any instructions of a specified type in a given
+  * chrome manifest.
+  *
+  * @param  aManifest
+  *         Manifest data, as returned by ChromeManifestParser.parseSync().
+  * @param  aType
+  *         Instruction type to filter by.
+  * @return True if any matching instructions were found in the manifest.
+  */
+  hasType: function CMP_hasType(aManifest, aType) {
+    return aManifest.some(function(aEntry) {
+      return aEntry.type == aType;
+    });
+  }
+};
--- a/toolkit/mozapps/extensions/Makefile.in
+++ b/toolkit/mozapps/extensions/Makefile.in
@@ -66,16 +66,17 @@ EXTRA_PP_JS_MODULES = \
   AddonManager.jsm \
   AddonRepository.jsm \
   AddonUpdateChecker.jsm \
   PluginProvider.jsm \
   XPIProvider.jsm \
   $(NULL)
 
 EXTRA_JS_MODULES = \
+  ChromeManifestParser.jsm \
   LightweightThemeManager.jsm \
   SpellCheckDictionaryBootstrap.js \
   $(NULL)
 
 ifdef ENABLE_TESTS
 DIRS += test
 endif
 
--- a/toolkit/mozapps/extensions/XPIProvider.jsm
+++ b/toolkit/mozapps/extensions/XPIProvider.jsm
@@ -16,16 +16,17 @@
 #
 # The Initial Developer of the Original Code is
 # the Mozilla Foundation.
 # Portions created by the Initial Developer are Copyright (C) 2009
 # the Initial Developer. All Rights Reserved.
 #
 # Contributor(s):
 #   Dave Townsend <dtownsend@oxymoronical.com>
+#   Blair McBride <bmcbride@mozilla.com>
 #
 # Alternatively, the contents of this file may be used under the terms of
 # either the GNU General Public License Version 2 or later (the "GPL"), or
 # the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
 # in which case the provisions of the GPL or the LGPL are applicable instead
 # of those above. If you wish to allow use of your version of this file only
 # under the terms of either the GPL or the LGPL, and not to allow others to
 # use your version of this file under the terms of the MPL, indicate your
@@ -43,16 +44,17 @@ const Cc = Components.classes;
 const Ci = Components.interfaces;
 const Cr = Components.results;
 
 var EXPORTED_SYMBOLS = [];
 
 Components.utils.import("resource://gre/modules/Services.jsm");
 Components.utils.import("resource://gre/modules/AddonManager.jsm");
 Components.utils.import("resource://gre/modules/AddonRepository.jsm");
+Components.utils.import("resource://gre/modules/ChromeManifestParser.jsm");
 Components.utils.import("resource://gre/modules/LightweightThemeManager.jsm");
 Components.utils.import("resource://gre/modules/FileUtils.jsm");
 Components.utils.import("resource://gre/modules/NetUtil.jsm");
 
 const PREF_DB_SCHEMA                  = "extensions.databaseSchema";
 const PREF_INSTALL_CACHE              = "extensions.installCache";
 const PREF_BOOTSTRAP_ADDONS           = "extensions.bootstrappedAddons";
 const PREF_PENDING_OPERATIONS         = "extensions.pendingOperations";
@@ -117,17 +119,17 @@ const PREFIX_ITEM_URI                 = 
 const RDFURI_ITEM_ROOT                = "urn:mozilla:item:root"
 const RDFURI_INSTALL_MANIFEST_ROOT    = "urn:mozilla:install-manifest";
 const PREFIX_NS_EM                    = "http://www.mozilla.org/2004/em-rdf#";
 
 const TOOLKIT_ID                      = "toolkit@mozilla.org";
 
 const BRANCH_REGEXP                   = /^([^\.]+\.[0-9]+[a-z]*).*/gi;
 
-const DB_SCHEMA                       = 6;
+const DB_SCHEMA                       = 7;
 const REQ_VERSION                     = 2;
 
 #ifdef MOZ_COMPATIBILITY_NIGHTLY
 const PREF_EM_CHECK_COMPATIBILITY = PREF_EM_CHECK_COMPATIBILITY_BASE +
                                     ".nightly";
 #else
 const PREF_EM_CHECK_COMPATIBILITY = PREF_EM_CHECK_COMPATIBILITY_BASE + "." +
                                     Services.appinfo.version.replace(BRANCH_REGEXP, "$1");
@@ -141,17 +143,18 @@ const PROP_LOCALE_SINGLE = ["name", "des
 const PROP_LOCALE_MULTI  = ["developers", "translators", "contributors"];
 const PROP_TARGETAPP     = ["id", "minVersion", "maxVersion"];
 
 // Properties that only exist in the database
 const DB_METADATA        = ["installDate", "updateDate", "size", "sourceURI",
                             "releaseNotesURI", "applyBackgroundUpdates"];
 const DB_BOOL_METADATA   = ["visible", "active", "userDisabled", "appDisabled",
                             "pendingUninstall", "bootstrap", "skinnable",
-                            "softDisabled", "foreignInstall"];
+                            "softDisabled", "foreignInstall",
+                            "hasBinaryComponents"];
 
 const BOOTSTRAP_REASONS = {
   APP_STARTUP     : 1,
   APP_SHUTDOWN    : 2,
   ADDON_ENABLE    : 3,
   ADDON_DISABLE   : 4,
   ADDON_INSTALL   : 5,
   ADDON_UNINSTALL : 6,
@@ -845,16 +848,23 @@ function loadManifestFromDir(aDir) {
   let bis = Cc["@mozilla.org/network/buffered-input-stream;1"].
             createInstance(Ci.nsIBufferedInputStream);
   bis.init(fis, 4096);
 
   try {
     let addon = loadManifestFromRDF(Services.io.newFileURI(file), bis);
     addon._sourceBundle = aDir.clone().QueryInterface(Ci.nsILocalFile);
     addon.size = getFileSize(aDir);
+
+    file = aDir.clone();
+    file.append("chrome.manifest");
+    let chromeManifest = ChromeManifestParser.parseSync(Services.io.newFileURI(file));
+    addon.hasBinaryComponents = ChromeManifestParser.hasType(chromeManifest,
+                                                             "binary-component");
+
     return addon;
   }
   finally {
     bis.close();
     fis.close();
   }
 }
 
@@ -877,16 +887,26 @@ function loadManifestFromZipReader(aZipR
     let addon = loadManifestFromRDF(uri, bis);
     addon._sourceBundle = aZipReader.file;
 
     addon.size = 0;
     let entries = aZipReader.findEntries(null);
     while (entries.hasMore())
       addon.size += aZipReader.getEntry(entries.getNext()).realSize;
 
+    // Binary components can only be loaded from unpacked addons.
+    if (addon.unpack) {
+      uri = buildJarURI(aZipReader.file, "chrome.manifest");
+      let chromeManifest = ChromeManifestParser.parseSync(uri);
+      addon.hasBinaryComponents = ChromeManifestParser.hasType(chromeManifest,
+                                                               "binary-component");
+    } else {
+      addon.hasBinaryComponents = false;
+    }
+
     return addon;
   }
   finally {
     bis.close();
     zis.close();
   }
 }
 
@@ -3875,17 +3895,17 @@ var XPIProvider = {
 };
 
 const FIELDS_ADDON = "internal_id, id, location, version, type, internalName, " +
                      "updateURL, updateKey, optionsURL, optionsType, aboutURL, " +
                      "iconURL, icon64URL, defaultLocale, visible, active, " +
                      "userDisabled, appDisabled, pendingUninstall, descriptor, " +
                      "installDate, updateDate, applyBackgroundUpdates, bootstrap, " +
                      "skinnable, size, sourceURI, releaseNotesURI, softDisabled, " +
-                     "foreignInstall";
+                     "foreignInstall, hasBinaryComponents";
 
 /**
  * A helper function to log an SQL error.
  *
  * @param  aError
  *         The storage error code associated with the error
  * @param  aErrorString
  *         An error message
@@ -4021,17 +4041,17 @@ var XPIDatabase = {
     addAddonMetadata_addon: "INSERT INTO addon VALUES (NULL, :id, :location, " +
                             ":version, :type, :internalName, :updateURL, " +
                             ":updateKey, :optionsURL, :optionsType, :aboutURL, " +
                             ":iconURL, :icon64URL, :locale, :visible, :active, " +
                             ":userDisabled, :appDisabled, :pendingUninstall, " +
                             ":descriptor, :installDate, :updateDate, " +
                             ":applyBackgroundUpdates, :bootstrap, :skinnable, " +
                             ":size, :sourceURI, :releaseNotesURI, :softDisabled, " +
-                            ":foreignInstall)",
+                            ":foreignInstall, :hasBinaryComponents)",
     addAddonMetadata_addon_locale: "INSERT INTO addon_locale VALUES " +
                                    "(:internal_id, :name, :locale)",
     addAddonMetadata_locale: "INSERT INTO locale (name, description, creator, " +
                              "homepageURL) VALUES (:name, :description, " +
                              ":creator, :homepageURL)",
     addAddonMetadata_strings: "INSERT INTO locale_strings VALUES (:locale, " +
                               ":type, :value)",
     addAddonMetadata_targetApplication: "INSERT INTO targetApplication VALUES " +
@@ -4555,16 +4575,17 @@ var XPIDatabase = {
                                   "userDisabled INTEGER, appDisabled INTEGER, " +
                                   "pendingUninstall INTEGER, descriptor TEXT, " +
                                   "installDate INTEGER, updateDate INTEGER, " +
                                   "applyBackgroundUpdates INTEGER, " +
                                   "bootstrap INTEGER, skinnable INTEGER, " +
                                   "size INTEGER, sourceURI TEXT, " +
                                   "releaseNotesURI TEXT, softDisabled INTEGER, " +
                                   "foreignInstall INTEGER, " +
+                                  "hasBinaryComponents INTEGER, " +
                                   "UNIQUE (id, location)");
       this.connection.createTable("targetApplication",
                                   "addon_internal_id INTEGER, " +
                                   "id TEXT, minVersion TEXT, maxVersion TEXT, " +
                                   "UNIQUE (addon_internal_id, id)");
       this.connection.createTable("targetPlatform",
                                   "addon_internal_id INTEGER, " +
                                   "os, abi TEXT, " +
@@ -6905,18 +6926,20 @@ AddonInternal.prototype = {
 
   isCompatibleWith: function(aAppVersion, aPlatformVersion) {
     let app = this.matchingTargetApplication;
     if (!app)
       return false;
 
     // Only extensions can be compatible by default; themes always use strict
     // compatibility checking.
-    if (this.type == "extension" && !AddonManager.strictCompatibility)
+    if (this.type == "extension" && !AddonManager.strictCompatibility &&
+        !this.hasBinaryComponents) {
       return true;
+    }
 
     if (!aAppVersion)
       aAppVersion = Services.appinfo.version;
     if (!aPlatformVersion)
       aPlatformVersion = Services.appinfo.platformVersion;
 
     let version;
     if (app.id == Services.appinfo.ID)
@@ -7123,17 +7146,18 @@ function AddonWrapper(aAddon) {
       return [repositoryAddon[aProp], true];
     }
 
     return [objValue, false];
   }
 
   ["id", "version", "type", "isCompatible", "isPlatformCompatible",
    "providesUpdatesSecurely", "blocklistState", "blocklistURL", "appDisabled",
-   "softDisabled", "skinnable", "size", "foreignInstall"].forEach(function(aProp) {
+   "softDisabled", "skinnable", "size", "foreignInstall", "hasBinaryComponents"
+   ].forEach(function(aProp) {
      this.__defineGetter__(aProp, function() aAddon[aProp]);
   }, this);
 
   ["fullDescription", "developerComments", "eula", "supportURL",
    "contributionURL", "contributionAmount", "averageRating", "reviewCount",
    "reviewURL", "totalDownloads", "weeklyDownloads", "dailyUsers",
    "repositoryStatus"].forEach(function(aProp) {
     this.__defineGetter__(aProp, function() {
new file mode 100644
--- /dev/null
+++ b/toolkit/mozapps/extensions/test/addons/test_chromemanifest_1/chrome.manifest
@@ -0,0 +1,6 @@
+content test-addon-1 chrome/content
+# comment!  
+  locale test-addon-1 en-US locale/en-US
+      # commentaire!  
+  locale test-addon-1    fr-FR locale/fr-FR  
+overlay	chrome://browser/content/browser.xul    chrome://test-addon-1/content/overlay.xul
new file mode 100644
--- /dev/null
+++ b/toolkit/mozapps/extensions/test/addons/test_chromemanifest_1/install.rdf
@@ -0,0 +1,23 @@
+<?xml version="1.0"?>
+
+<RDF xmlns="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
+     xmlns:em="http://www.mozilla.org/2004/em-rdf#">
+
+  <Description about="urn:mozilla:install-manifest">
+    <em:id>addon1@tests.mozilla.org</em:id>
+    <em:version>1.0</em:version>
+
+    <!-- Front End MetaData -->
+    <em:name>Test 1</em:name>
+    <em:description>Test Description</em:description>
+
+    <em:targetApplication>
+      <Description>
+        <em:id>xpcshell@tests.mozilla.org</em:id>
+        <em:minVersion>1</em:minVersion>
+        <em:maxVersion>2</em:maxVersion>
+      </Description>
+    </em:targetApplication>
+
+  </Description>
+</RDF>
new file mode 100644
--- /dev/null
+++ b/toolkit/mozapps/extensions/test/addons/test_chromemanifest_2/chrome.manifest
@@ -0,0 +1,7 @@
+content test-addon-1 chrome/content
+  
+  locale test-addon-1 en-US locale/en-US
+  locale test-addon-1    fr-FR locale/fr-FR  
+overlay	chrome://browser/content/browser.xul    chrome://test-addon-1/content/overlay.xul
+binary-component components/something.so
+manifest thisdoesntexist.manifest
new file mode 100644
--- /dev/null
+++ b/toolkit/mozapps/extensions/test/addons/test_chromemanifest_2/install.rdf
@@ -0,0 +1,24 @@
+<?xml version="1.0"?>
+
+<RDF xmlns="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
+     xmlns:em="http://www.mozilla.org/2004/em-rdf#">
+
+  <Description about="urn:mozilla:install-manifest">
+    <em:id>addon2@tests.mozilla.org</em:id>
+    <em:version>1.0</em:version>
+
+    <!-- Front End MetaData -->
+    <em:name>Test 2</em:name>
+    <em:description>Test Description</em:description>
+    <em:unpack>true</em:unpack>
+
+    <em:targetApplication>
+      <Description>
+        <em:id>xpcshell@tests.mozilla.org</em:id>
+        <em:minVersion>1</em:minVersion>
+        <em:maxVersion>2</em:maxVersion>
+      </Description>
+    </em:targetApplication>
+
+  </Description>
+</RDF>
new file mode 100644
--- /dev/null
+++ b/toolkit/mozapps/extensions/test/addons/test_chromemanifest_3/chrome.manifest
@@ -0,0 +1,9 @@
+content test-addon-1 chrome/content
+  
+  locale test-addon-1 en-US locale/en-US
+  locale test-addon-1    fr-FR locale/fr-FR  
+overlay	chrome://browser/content/browser.xul    chrome://test-addon-1/content/overlay.xul
+   
+     binary-component   components/something.so  
+
+ manifest  jar:inner.jar!/nested.manifest
new file mode 100644
index e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..b4a40052f4279e0437d488dfc1569461dfede19b
GIT binary patch
literal 180
zc$^FHW@Zs#U|`^2C~1thS5H{gZUW@l05Lxh=cN{xq^9WQCgx?P0hz&J1>ydlYeG)=
z>Uds#{4^-w#7Td@GoGOup1OJ$eSJJnp1*7w!q9ZZCU(EZiY0b44xeUa2=HcP5@En?
b1qd)QC_q_g76y2;vOyFuGS~v?M6e<NW%Vup
new file mode 100644
--- /dev/null
+++ b/toolkit/mozapps/extensions/test/addons/test_chromemanifest_3/install.rdf
@@ -0,0 +1,24 @@
+<?xml version="1.0"?>
+
+<RDF xmlns="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
+     xmlns:em="http://www.mozilla.org/2004/em-rdf#">
+
+  <Description about="urn:mozilla:install-manifest">
+    <em:id>addon3@tests.mozilla.org</em:id>
+    <em:version>1.0</em:version>
+
+    <!-- Front End MetaData -->
+    <em:name>Test 3</em:name>
+    <em:description>Test Description</em:description>
+    <em:unpack>true</em:unpack>
+
+    <em:targetApplication>
+      <Description>
+        <em:id>xpcshell@tests.mozilla.org</em:id>
+        <em:minVersion>1</em:minVersion>
+        <em:maxVersion>1</em:maxVersion>
+      </Description>
+    </em:targetApplication>
+
+  </Description>
+</RDF>
new file mode 100644
--- /dev/null
+++ b/toolkit/mozapps/extensions/test/addons/test_chromemanifest_4/chrome.manifest
@@ -0,0 +1,6 @@
+content test-addon-1 chrome/content
+  
+  locale test-addon-1 en-US locale/en-US
+  locale test-addon-1    fr-FR locale/fr-FR  
+overlay	chrome://browser/content/browser.xul    chrome://test-addon-1/content/overlay.xul
+   manifest     components/components.manifest  
new file mode 100644
--- /dev/null
+++ b/toolkit/mozapps/extensions/test/addons/test_chromemanifest_4/components/components.manifest
@@ -0,0 +1,2 @@
+binary-component mycomponent.dll
+manifest other/something.manifest
new file mode 100644
--- /dev/null
+++ b/toolkit/mozapps/extensions/test/addons/test_chromemanifest_4/components/other/something.manifest
@@ -0,0 +1,1 @@
+binary-component thermalnuclearwar.dll
new file mode 100644
--- /dev/null
+++ b/toolkit/mozapps/extensions/test/addons/test_chromemanifest_4/install.rdf
@@ -0,0 +1,24 @@
+<?xml version="1.0"?>
+
+<RDF xmlns="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
+     xmlns:em="http://www.mozilla.org/2004/em-rdf#">
+
+  <Description about="urn:mozilla:install-manifest">
+    <em:id>addon4@tests.mozilla.org</em:id>
+    <em:version>1.0</em:version>
+
+    <!-- Front End MetaData -->
+    <em:name>Test 4</em:name>
+    <em:description>Test Description</em:description>
+    <em:unpack>true</em:unpack>
+
+    <em:targetApplication>
+      <Description>
+        <em:id>xpcshell@tests.mozilla.org</em:id>
+        <em:minVersion>1</em:minVersion>
+        <em:maxVersion>1</em:maxVersion>
+      </Description>
+    </em:targetApplication>
+
+  </Description>
+</RDF>
new file mode 100644
--- /dev/null
+++ b/toolkit/mozapps/extensions/test/addons/test_chromemanifest_5/chrome.manifest
@@ -0,0 +1,7 @@
+content test-addon-1 chrome/content
+  
+  locale test-addon-1 en-US locale/en-US
+  locale test-addon-1    fr-FR locale/fr-FR  
+overlay	chrome://browser/content/browser.xul    chrome://test-addon-1/content/overlay.xul
+   
+     binary-component   components/something.so  
new file mode 100644
--- /dev/null
+++ b/toolkit/mozapps/extensions/test/addons/test_chromemanifest_5/install.rdf
@@ -0,0 +1,24 @@
+<?xml version="1.0"?>
+
+<RDF xmlns="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
+     xmlns:em="http://www.mozilla.org/2004/em-rdf#">
+
+  <Description about="urn:mozilla:install-manifest">
+    <em:id>addon5@tests.mozilla.org</em:id>
+    <em:version>1.0</em:version>
+
+    <!-- Front End MetaData -->
+    <em:name>Test 5</em:name>
+    <em:description>Test Description</em:description>
+    <em:unpack>false</em:unpack>
+
+    <em:targetApplication>
+      <Description>
+        <em:id>xpcshell@tests.mozilla.org</em:id>
+        <em:minVersion>1</em:minVersion>
+        <em:maxVersion>2</em:maxVersion>
+      </Description>
+    </em:targetApplication>
+
+  </Description>
+</RDF>
new file mode 100644
--- /dev/null
+++ b/toolkit/mozapps/extensions/test/addons/test_migrate8/chrome.manifest
@@ -0,0 +1,6 @@
+content test-addon-1 chrome/content
+  
+  locale test-addon-1 en-US locale/en-US
+  locale test-addon-1    fr-FR locale/fr-FR  
+overlay	chrome://browser/content/browser.xul    chrome://test-addon-1/content/overlay.xul
+binary-component components/something.so
new file mode 100644
--- /dev/null
+++ b/toolkit/mozapps/extensions/test/addons/test_migrate8/install.rdf
@@ -0,0 +1,24 @@
+<?xml version="1.0"?>
+
+<RDF xmlns="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
+     xmlns:em="http://www.mozilla.org/2004/em-rdf#">
+
+  <Description about="urn:mozilla:install-manifest">
+    <em:id>addon8@tests.mozilla.org</em:id>
+    <em:version>1.0</em:version>
+
+    <!-- Front End MetaData -->
+    <em:name>Test 8</em:name>
+    <em:description>Test Description</em:description>
+    <em:unpack>true</em:unpack>
+
+    <em:targetApplication>
+      <Description>
+        <em:id>xpcshell@tests.mozilla.org</em:id>
+        <em:minVersion>1</em:minVersion>
+        <em:maxVersion>2</em:maxVersion>
+      </Description>
+    </em:targetApplication>
+
+  </Description>
+</RDF>
new file mode 100644
--- /dev/null
+++ b/toolkit/mozapps/extensions/test/xpcshell/test_ChromeManifestParser.js
@@ -0,0 +1,108 @@
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/
+ */
+
+// Tests ChromeManifestParser.js
+
+Components.utils.import("resource://gre/modules/ChromeManifestParser.jsm");
+
+
+function run_test() {
+  do_test_pending();
+  createAppInfo("xpcshell@tests.mozilla.org", "XPCShell", "2", "2");
+
+  startupManager();
+  
+  installAllFiles([do_get_addon("test_chromemanifest_1"),
+                   do_get_addon("test_chromemanifest_2"),
+                   do_get_addon("test_chromemanifest_3"),
+                   do_get_addon("test_chromemanifest_4")],
+                  function() {
+
+    restartManager();
+    run_test_1();
+  });
+}
+
+function run_test_1() {
+  AddonManager.getAddonsByIDs(["addon1@tests.mozilla.org",
+                               "addon2@tests.mozilla.org",
+                               "addon3@tests.mozilla.org",
+                               "addon4@tests.mozilla.org"],
+                              function([a1, a2, a3, a4]) {
+    // addon1
+    let a1Uri = a1.getResourceURI("/").spec;
+    let expected = [
+      {type: "content", baseURI: a1Uri, args: ["test-addon-1", "chrome/content"]},
+      {type: "locale", baseURI: a1Uri, args: ["test-addon-1", "en-US", "locale/en-US"]},
+      {type: "locale", baseURI: a1Uri, args: ["test-addon-1", "fr-FR", "locale/fr-FR"]},
+      {type: "overlay", baseURI: a1Uri, args: ["chrome://browser/content/browser.xul", "chrome://test-addon-1/content/overlay.xul"]}
+    ];
+    let manifestURI = a1.getResourceURI("chrome.manifest");
+    let manifest = ChromeManifestParser.parseSync(manifestURI);
+
+    do_check_true(Array.isArray(manifest));
+    do_check_eq(manifest.length, expected.length);
+    for (let i = 0; i < manifest.length; i++) {
+      do_check_eq(JSON.stringify(manifest[i]), JSON.stringify(expected[i]));
+    }
+
+    // addon2
+    let a2Uri = a2.getResourceURI("/").spec;
+    expected = [
+      {type: "content", baseURI: a2Uri, args: ["test-addon-1", "chrome/content"]},
+      {type: "locale", baseURI: a2Uri, args: ["test-addon-1", "en-US", "locale/en-US"]},
+      {type: "locale", baseURI: a2Uri, args: ["test-addon-1", "fr-FR", "locale/fr-FR"]},
+      {type: "overlay", baseURI: a2Uri, args: ["chrome://browser/content/browser.xul", "chrome://test-addon-1/content/overlay.xul"]},
+      {type: "binary-component", baseURI: a2Uri, args: ["components/something.so"]}
+    ];
+    manifestURI = a2.getResourceURI("chrome.manifest");
+    manifest = ChromeManifestParser.parseSync(manifestURI);
+
+    do_check_true(Array.isArray(manifest));
+    do_check_eq(manifest.length, expected.length);
+    for (let i = 0; i < manifest.length; i++) {
+      do_check_eq(JSON.stringify(manifest[i]), JSON.stringify(expected[i]));
+    }
+
+    // addon3
+    let a3Uri = a3.getResourceURI("/").spec;
+    expected = [
+      {type: "content", baseURI: a3Uri, args: ["test-addon-1", "chrome/content"]},
+      {type: "locale", baseURI: a3Uri, args: ["test-addon-1", "en-US", "locale/en-US"]},
+      {type: "locale", baseURI: a3Uri, args: ["test-addon-1", "fr-FR", "locale/fr-FR"]},
+      {type: "overlay", baseURI: a3Uri, args: ["chrome://browser/content/browser.xul", "chrome://test-addon-1/content/overlay.xul"]},
+      {type: "binary-component", baseURI: a3Uri, args: ["components/something.so"]},
+      {type: "locale", baseURI: "jar:" + a3.getResourceURI("/inner.jar").spec + "!/", args: ["test-addon-1", "en-NZ", "locale/en-NZ"]},
+    ];
+    manifestURI = a3.getResourceURI("chrome.manifest");
+    manifest = ChromeManifestParser.parseSync(manifestURI);
+
+    do_check_true(Array.isArray(manifest));
+    do_check_eq(manifest.length, expected.length);
+    for (let i = 0; i < manifest.length; i++) {
+      do_check_eq(JSON.stringify(manifest[i]), JSON.stringify(expected[i]));
+    }
+
+    // addon4
+    let a4Uri = a4.getResourceURI("/").spec;
+    expected = [
+      {type: "content", baseURI: a4Uri, args: ["test-addon-1", "chrome/content"]},
+      {type: "locale", baseURI: a4Uri, args: ["test-addon-1", "en-US", "locale/en-US"]},
+      {type: "locale", baseURI: a4Uri, args: ["test-addon-1", "fr-FR", "locale/fr-FR"]},
+      {type: "overlay", baseURI: a4Uri, args: ["chrome://browser/content/browser.xul", "chrome://test-addon-1/content/overlay.xul"]},
+      {type: "binary-component", baseURI: a4.getResourceURI("components/").spec, args: ["mycomponent.dll"]},
+      {type: "binary-component", baseURI: a4.getResourceURI("components/other/").spec, args: ["thermalnuclearwar.dll"]}
+    ];
+    manifestURI = a4.getResourceURI("chrome.manifest");
+    manifest = ChromeManifestParser.parseSync(manifestURI);
+
+    do_check_true(Array.isArray(manifest));
+    do_check_eq(manifest.length, expected.length);
+    for (let i = 0; i < manifest.length; i++) {
+      do_check_eq(JSON.stringify(manifest[i]), JSON.stringify(expected[i]));
+    }
+
+    do_test_finished();
+  });
+}
new file mode 100644
--- /dev/null
+++ b/toolkit/mozapps/extensions/test/xpcshell/test_hasbinarycomponents.js
@@ -0,0 +1,82 @@
+/* Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/
+ */
+
+// Tests detection of binary components via parsing of chrome manifests.
+
+const profileDir = gProfD.clone();
+profileDir.append("extensions");
+
+function run_test() {
+  do_test_pending();
+  createAppInfo("xpcshell@tests.mozilla.org", "XPCShell", "2", "2");
+
+  startupManager();
+  
+  installAllFiles([do_get_addon("test_chromemanifest_1"),
+                   do_get_addon("test_chromemanifest_2"),
+                   do_get_addon("test_chromemanifest_3"),
+                   do_get_addon("test_chromemanifest_4"),
+                   do_get_addon("test_chromemanifest_5")],
+                  function() {
+
+    restartManager();
+
+    AddonManager.getAddonsByIDs(["addon1@tests.mozilla.org",
+                                 "addon2@tests.mozilla.org",
+                                 "addon3@tests.mozilla.org",
+                                 "addon4@tests.mozilla.org",
+                                 "addon5@tests.mozilla.org"],
+                                function([a1, a2, a3, a4, a5]) {
+      // addon1 has no binary components
+      do_check_neq(a1, null);
+      do_check_false(a1.userDisabled);
+      do_check_false(a1.hasBinaryComponents);
+      do_check_true(a1.isCompatible);
+      do_check_false(a1.appDisabled);
+      do_check_true(a1.isActive);
+      do_check_true(isExtensionInAddonsList(profileDir, a1.id));
+
+      // addon2 has a binary component, is compatible
+      do_check_neq(a2, null);
+      do_check_false(a2.userDisabled);
+      do_check_true(a2.hasBinaryComponents);
+      do_check_true(a2.isCompatible);
+      do_check_false(a2.appDisabled);
+      do_check_true(a2.isActive);
+      do_check_true(isExtensionInAddonsList(profileDir, a2.id));
+
+      // addon3 has a binary component, is incompatible
+      do_check_neq(a3, null);
+      do_check_false(a3.userDisabled);
+      do_check_true(a2.hasBinaryComponents);
+      do_check_false(a3.isCompatible);
+      do_check_true(a3.appDisabled);
+      do_check_false(a3.isActive);
+      do_check_false(isExtensionInAddonsList(profileDir, a3.id));
+
+      // addon4 has a binary component listed in a sub-manifest, is incompatible
+      do_check_neq(a4, null);
+      do_check_false(a4.userDisabled);
+      do_check_true(a2.hasBinaryComponents);
+      do_check_false(a4.isCompatible);
+      do_check_true(a4.appDisabled);
+      do_check_false(a4.isActive);
+      do_check_false(isExtensionInAddonsList(profileDir, a4.id));
+
+      // addon5 has a binary component, but is set to not unpack
+      do_check_neq(a5, null);
+      do_check_false(a5.userDisabled);
+      if (TEST_UNPACKED)
+        do_check_true(a5.hasBinaryComponents);
+      else
+        do_check_false(a5.hasBinaryComponents);
+      do_check_true(a5.isCompatible);
+      do_check_false(a5.appDisabled);
+      do_check_true(a5.isActive);
+      do_check_true(isExtensionInAddonsList(profileDir, a5.id));
+
+      do_test_finished();
+    });
+  });
+}
--- a/toolkit/mozapps/extensions/test/xpcshell/test_migrate1.js
+++ b/toolkit/mozapps/extensions/test/xpcshell/test_migrate1.js
@@ -111,16 +111,23 @@ function run_test() {
 
   stagedXPIs.append("addon7@tests.mozilla.org");
   stagedXPIs.create(AM_Ci.nsIFile.DIRECTORY_TYPE, 0755);
 
   let addon7 = do_get_addon("test_migrate7");
   addon7.copyTo(stagedXPIs, "tmp.xpi");
   stagedXPIs = stagedXPIs.parent;
 
+  stagedXPIs.append("addon8@tests.mozilla.org");
+  stagedXPIs.create(AM_Ci.nsIFile.DIRECTORY_TYPE, 0755);
+
+  let addon7 = do_get_addon("test_migrate8");
+  addon7.copyTo(stagedXPIs, "tmp.xpi");
+  stagedXPIs = stagedXPIs.parent;
+
   let old = do_get_file("data/test_migrate.rdf");
   old.copyTo(gProfD, "extensions.rdf");
 
   let oldCache = gProfD.clone();
   oldCache.append("extensions.cache");
   oldCache.create(AM_Ci.nsIFile.NORMAL_FILE_TYPE, FileUtils.PERMS_FILE);
 
   // Theme state is determined by the selected theme pref
@@ -139,75 +146,92 @@ function run_test() {
 
   AddonManager.getAddonsByIDs(["addon1@tests.mozilla.org",
                                "addon2@tests.mozilla.org",
                                "addon3@tests.mozilla.org",
                                "addon4@tests.mozilla.org",
                                "addon5@tests.mozilla.org",
                                "addon6@tests.mozilla.org",
                                "addon7@tests.mozilla.org",
+                               "addon8@tests.mozilla.org",
                                "theme1@tests.mozilla.org",
                                "theme2@tests.mozilla.org"], function([a1, a2, a3,
                                                                       a4, a5, a6,
-                                                                      a7, t1, t2]) {
+                                                                      a7, a8, t1,
+                                                                      t2]) {
     // addon1 was user and app enabled in the old extensions.rdf
     do_check_neq(a1, null);
     do_check_false(a1.userDisabled);
     do_check_false(a1.appDisabled);
     do_check_true(a1.isActive);
     do_check_true(isExtensionInAddonsList(profileDir, a1.id));
+    do_check_false(a1.hasBinaryComponents);
 
     // addon2 was user disabled and app enabled in the old extensions.rdf
     do_check_neq(a2, null);
     do_check_true(a2.userDisabled);
     do_check_false(a2.appDisabled);
     do_check_false(a2.isActive);
     do_check_false(isExtensionInAddonsList(profileDir, a2.id));
+    do_check_false(a2.hasBinaryComponents);
 
     // addon3 was pending user disable and app disabled in the old extensions.rdf
     do_check_neq(a3, null);
     do_check_true(a3.userDisabled);
     do_check_true(a3.appDisabled);
     do_check_false(a3.isActive);
     do_check_false(isExtensionInAddonsList(profileDir, a3.id));
+    do_check_false(a3.hasBinaryComponents);
 
     // addon4 was pending user enable and app disabled in the old extensions.rdf
     do_check_neq(a4, null);
     do_check_false(a4.userDisabled);
     do_check_true(a4.appDisabled);
     do_check_false(a4.isActive);
     do_check_false(isExtensionInAddonsList(profileDir, a4.id));
+    do_check_false(a4.hasBinaryComponents);
 
     // addon5 was disabled and compatible but a new version has been installed
     // since, it should still be disabled but should be incompatible
     do_check_neq(a5, null);
     do_check_true(a5.userDisabled);
     do_check_true(a5.appDisabled);
     do_check_false(a5.isActive);
     do_check_false(isExtensionInAddonsList(profileDir, a5.id));
+    do_check_false(a5.hasBinaryComponents);
 
     // addon6 should be installed and compatible and packed unless unpacking is
     // forced
     do_check_neq(a6, null);
     do_check_false(a6.userDisabled);
     do_check_false(a6.appDisabled);
     do_check_true(a6.isActive);
     do_check_true(isExtensionInAddonsList(profileDir, a6.id));
     if (Services.prefs.getBoolPref("extensions.alwaysUnpack"))
       do_check_eq(a6.getResourceURI("install.rdf").scheme, "file");
     else
       do_check_eq(a6.getResourceURI("install.rdf").scheme, "jar");
+    do_check_false(a6.hasBinaryComponents);
 
     // addon7 should be installed and compatible and unpacked
     do_check_neq(a7, null);
     do_check_false(a7.userDisabled);
     do_check_false(a7.appDisabled);
     do_check_true(a7.isActive);
     do_check_true(isExtensionInAddonsList(profileDir, a7.id));
     do_check_eq(a7.getResourceURI("install.rdf").scheme, "file");
+    do_check_false(a7.hasBinaryComponents);
+
+    // addon8 should be installed and compatible and have binary components
+    do_check_neq(a8, null);
+    do_check_false(a8.userDisabled);
+    do_check_false(a8.appDisabled);
+    do_check_true(a8.isActive);
+    do_check_true(isExtensionInAddonsList(profileDir, a8.id));
+    do_check_true(a8.hasBinaryComponents);
 
     // Theme 1 was previously enabled
     do_check_neq(t1, null);
     do_check_false(t1.userDisabled);
     do_check_false(t1.appDisabled);
     do_check_true(t1.isActive);
     do_check_true(isThemeInAddonsList(profileDir, t1.id));
     do_check_false(hasFlag(t1.permissions, AddonManager.PERM_CAN_ENABLE));
--- a/toolkit/mozapps/extensions/test/xpcshell/test_migrate4.js
+++ b/toolkit/mozapps/extensions/test/xpcshell/test_migrate4.js
@@ -93,56 +93,61 @@ function prepare_profile() {
   writeInstallRDFForExtension(addon1, profileDir);
   writeInstallRDFForExtension(addon2, profileDir);
   writeInstallRDFForExtension(addon3, profileDir);
   writeInstallRDFForExtension(addon4, profileDir);
   writeInstallRDFForExtension(addon5, profileDir);
   writeInstallRDFForExtension(addon6, profileDir);
 
   startupManager();
-  AddonManager.getAddonsByIDs(["addon1@tests.mozilla.org",
-                               "addon2@tests.mozilla.org",
-                               "addon3@tests.mozilla.org",
-                               "addon4@tests.mozilla.org",
-                               "addon5@tests.mozilla.org",
-                               "addon6@tests.mozilla.org"],
-                               function([a1, a2, a3, a4, a5, a6]) {
-    a2.userDisabled = true;
-    a2.applyBackgroundUpdates = false;
-    a4.userDisabled = true;
-    a6.userDisabled = true;
-
-    a6.findUpdates({
-      onUpdateAvailable: function(aAddon, aInstall6) {
-        AddonManager.getInstallForURL("http://localhost:4444/addons/test_migrate4_7.xpi", function(aInstall7) {
-          completeAllInstalls([aInstall6, aInstall7], function() {
-            restartManager();
+  installAllFiles([do_get_addon("test_migrate8")],
+                  function() {
+    restartManager();
 
-            AddonManager.getAddonsByIDs(["addon1@tests.mozilla.org",
-                                         "addon2@tests.mozilla.org",
-                                         "addon3@tests.mozilla.org",
-                                         "addon4@tests.mozilla.org",
-                                         "addon5@tests.mozilla.org",
-                                         "addon6@tests.mozilla.org"],
-                                         function([a1, a2, a3, a4, a5, a6]) {
-              a3.userDisabled = true;
-              a4.userDisabled = false;
-
-              a5.findUpdates({
-                onUpdateFinished: function() {
-                  shutdownManager();
-
-                  perform_migration();
-                }
-              }, AddonManager.UPDATE_WHEN_USER_REQUESTED);
+    AddonManager.getAddonsByIDs(["addon1@tests.mozilla.org",
+                                 "addon2@tests.mozilla.org",
+                                 "addon3@tests.mozilla.org",
+                                 "addon4@tests.mozilla.org",
+                                 "addon5@tests.mozilla.org",
+                                 "addon6@tests.mozilla.org"],
+                                 function([a1, a2, a3, a4, a5, a6]) {
+      a2.userDisabled = true;
+      a2.applyBackgroundUpdates = false;
+      a4.userDisabled = true;
+      a6.userDisabled = true;
+  
+      a6.findUpdates({
+        onUpdateAvailable: function(aAddon, aInstall6) {
+          AddonManager.getInstallForURL("http://localhost:4444/addons/test_migrate4_7.xpi", function(aInstall7) {
+            completeAllInstalls([aInstall6, aInstall7], function() {
+              restartManager();
+  
+              AddonManager.getAddonsByIDs(["addon1@tests.mozilla.org",
+                                           "addon2@tests.mozilla.org",
+                                           "addon3@tests.mozilla.org",
+                                           "addon4@tests.mozilla.org",
+                                           "addon5@tests.mozilla.org",
+                                           "addon6@tests.mozilla.org"],
+                                           function([a1, a2, a3, a4, a5, a6]) {
+                a3.userDisabled = true;
+                a4.userDisabled = false;
+  
+                a5.findUpdates({
+                  onUpdateFinished: function() {
+                    shutdownManager();
+  
+                    perform_migration();
+                  }
+                }, AddonManager.UPDATE_WHEN_USER_REQUESTED);
+              });
             });
-          });
-        }, "application/x-xpinstall");
-      }
-    }, AddonManager.UPDATE_WHEN_USER_REQUESTED);
+          }, "application/x-xpinstall");
+        }
+      }, AddonManager.UPDATE_WHEN_USER_REQUESTED);
+    });
   });
 }
 
 function perform_migration() {
   let dbfile = gProfD.clone();
   dbfile.append("extensions.sqlite");
   let db = AM_Cc["@mozilla.org/storage/service;1"].
            getService(AM_Ci.mozIStorageService).
@@ -164,79 +169,95 @@ function test_results() {
   check_startup_changes("enabled", []);
 
   AddonManager.getAddonsByIDs(["addon1@tests.mozilla.org",
                                "addon2@tests.mozilla.org",
                                "addon3@tests.mozilla.org",
                                "addon4@tests.mozilla.org",
                                "addon5@tests.mozilla.org",
                                "addon6@tests.mozilla.org",
-                               "addon7@tests.mozilla.org"],
-                               function([a1, a2, a3, a4, a5, a6, a7]) {
+                               "addon7@tests.mozilla.org",
+                               "addon8@tests.mozilla.org"],
+                               function([a1, a2, a3, a4, a5, a6, a7, a8]) {
     // addon1 was enabled
     do_check_neq(a1, null);
     do_check_false(a1.userDisabled);
     do_check_false(a1.appDisabled);
     do_check_true(a1.isActive);
     do_check_true(a1.applyBackgroundUpdates);
     do_check_true(a1.foreignInstall);
+    do_check_false(a1.hasBinaryComponents);
 
     // addon2 was disabled
     do_check_neq(a2, null);
     do_check_true(a2.userDisabled);
     do_check_false(a2.appDisabled);
     do_check_false(a2.isActive);
     do_check_false(a2.applyBackgroundUpdates);
     do_check_true(a2.foreignInstall);
+    do_check_false(a2.hasBinaryComponents);
 
     // addon3 was pending-disable in the database
     do_check_neq(a3, null);
     do_check_true(a3.userDisabled);
     do_check_false(a3.appDisabled);
     do_check_false(a3.isActive);
     do_check_true(a3.applyBackgroundUpdates);
     do_check_true(a3.foreignInstall);
+    do_check_false(a3.hasBinaryComponents);
 
     // addon4 was pending-enable in the database
     do_check_neq(a4, null);
     do_check_false(a4.userDisabled);
     do_check_false(a4.appDisabled);
     do_check_true(a4.isActive);
     do_check_true(a4.applyBackgroundUpdates);
     do_check_true(a4.foreignInstall);
+    do_check_false(a4.hasBinaryComponents);
 
     // addon5 was enabled in the database but needed a compatibiltiy update
     do_check_neq(a5, null);
     do_check_false(a5.userDisabled);
     do_check_false(a5.appDisabled);
     do_check_true(a5.isActive);
     do_check_true(a5.applyBackgroundUpdates);
     do_check_true(a5.foreignInstall);
+    do_check_false(a5.hasBinaryComponents);
 
     // addon6 was disabled and compatible but a new version has been installed
     do_check_neq(a6, null);
     do_check_eq(a6.version, "2.0");
     do_check_true(a6.userDisabled);
     do_check_false(a6.appDisabled);
     do_check_false(a6.isActive);
     do_check_true(a6.applyBackgroundUpdates);
     do_check_true(a6.foreignInstall);
     do_check_eq(a6.sourceURI.spec, "http://localhost:4444/addons/test_migrate4_6.xpi");
     do_check_eq(a6.releaseNotesURI.spec, "http://example.com/updateInfo.xhtml");
+    do_check_false(a6.hasBinaryComponents);
 
     // addon7 was installed manually
     do_check_neq(a7, null);
     do_check_eq(a7.version, "1.0");
     do_check_false(a7.userDisabled);
     do_check_false(a7.appDisabled);
     do_check_true(a7.isActive);
     do_check_true(a7.applyBackgroundUpdates);
     do_check_false(a7.foreignInstall);
     do_check_eq(a7.sourceURI.spec, "http://localhost:4444/addons/test_migrate4_7.xpi");
     do_check_eq(a7.releaseNotesURI, null);
+    do_check_false(a7.hasBinaryComponents);
+
+    // addon8 was enabled and has binary components
+    do_check_neq(a8, null);
+    do_check_false(a8.userDisabled);
+    do_check_false(a8.appDisabled);
+    do_check_true(a8.isActive);
+    do_check_true(a8.hasBinaryComponents);
+
     testserver.stop(do_test_finished);
   });
 }
 
 function run_test() {
   do_test_pending();
 
   prepare_profile();
--- a/toolkit/mozapps/extensions/test/xpcshell/xpcshell.ini
+++ b/toolkit/mozapps/extensions/test/xpcshell/xpcshell.ini
@@ -121,16 +121,17 @@ fail-if = os == "android"
 fail-if = os == "android"
 [test_bug619730.js]
 [test_bug620837.js]
 [test_bug655254.js]
 [test_bug659772.js]
 [test_bug675371.js]
 [test_cacheflush.js]
 [test_checkcompatibility.js]
+[test_ChromeManifestParser.js]
 [test_corrupt.js]
 [test_corrupt_strictcompat.js]
 [test_dictionary.js]
 [test_disable.js]
 [test_distribution.js]
 [test_dss.js]
 # Bug 676992: test consistently fails on Android
 fail-if = os == "android"
@@ -152,16 +153,17 @@ skip-if = os == "android"
 [test_gfxBlacklist_Equal_DriverOld.js]
 [test_gfxBlacklist_Equal_OK.js]
 [test_gfxBlacklist_GTE_DriverOld.js]
 [test_gfxBlacklist_GTE_OK.js]
 [test_gfxBlacklist_OK.js]
 [test_gfxBlacklist_OS.js]
 [test_gfxBlacklist_Vendor.js]
 [test_gfxBlacklist_prefs.js]
+[test_hasbinarycomponents.js]
 [test_install.js]
 # Bug 676992: test consistently hangs on Android
 skip-if = os == "android"
 [test_install_strictcompat.js]
 # Bug 676992: test consistently hangs on Android
 skip-if = os == "android"
 [test_locale.js]
 [test_locked.js]