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 80817 6b7874f5b777f0d61c955cac2c4f5bb1c8974205
parent 80816 1fb831781a15926b4d4969621f18f0937170b28e
child 80818 41c52a9e1337163736f5e297478ee773567b7484
push id434
push userclegnitto@mozilla.com
push dateWed, 21 Dec 2011 12:10:54 +0000
treeherdermozilla-beta@bddb6ed8dd47 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersdtownsend
bugs693899
milestone10.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
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]