Bug 693743: Some 3rd party add-ons are getting installed into the profile and are not disabled on start-up. r=Unfocused, a=LegNeato
authorDave Townsend <dtownsend@oxymoronical.com>
Wed, 26 Oct 2011 11:02:45 -0700
changeset 76385 fe76e382356c91fb974cf155d4be6c714a6c5e6d
parent 76384 690543d90329f329e33ad7e554795eb801225400
child 76386 efb42ace58b05ecfabb8073b632bcb804d33829c
push id314
push userdtownsend@mozilla.com
push dateWed, 26 Oct 2011 19:52:14 +0000
treeherdermozilla-beta@fe76e382356c [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersUnfocused, LegNeato
bugs693743
milestone8.0
Bug 693743: Some 3rd party add-ons are getting installed into the profile and are not disabled on start-up. r=Unfocused, a=LegNeato
browser/app/profile/firefox.js
build/automation.py.in
testing/mochitest/runtests.py
toolkit/mozapps/extensions/LightweightThemeManager.jsm
toolkit/mozapps/extensions/PluginProvider.jsm
toolkit/mozapps/extensions/XPIProvider.jsm
toolkit/mozapps/extensions/test/addons/test_migrate4_7/install.rdf
toolkit/mozapps/extensions/test/xpcshell/test_distribution.js
toolkit/mozapps/extensions/test/xpcshell/test_install.js
toolkit/mozapps/extensions/test/xpcshell/test_migrate4.js
toolkit/mozapps/extensions/test/xpcshell/test_startup.js
toolkit/mozapps/extensions/test/xpcshell/test_theme.js
toolkit/mozapps/extensions/test/xpcshell/test_update.js
--- a/browser/app/profile/firefox.js
+++ b/browser/app/profile/firefox.js
@@ -72,17 +72,17 @@ pref("extensions.blocklist.url", "https:
 pref("extensions.blocklist.detailsURL", "https://www.mozilla.com/%LOCALE%/blocklist/");
 pref("extensions.blocklist.itemURL", "https://addons.mozilla.org/%LOCALE%/%APP%/blocked/%blockID%");
 
 pref("extensions.update.autoUpdateDefault", true);
 
 // Disable add-ons installed into the shared user and shared system areas by
 // default. This does not include the application directory. See the SCOPE
 // constants in AddonManager.jsm for values to use here
-pref("extensions.autoDisableScopes", 10);
+pref("extensions.autoDisableScopes", 15);
 
 // Dictionary download preference
 pref("browser.dictionaries.download.url", "https://addons.mozilla.org/%LOCALE%/firefox/dictionaries/");
 
 // The minimum delay in seconds for the timer to fire.
 // default=2 minutes
 pref("app.update.timerMinimumDelay", 120);
 
--- a/build/automation.py.in
+++ b/build/automation.py.in
@@ -928,17 +928,17 @@ user_pref("camino.use_system_proxy_setti
                      extension, if extensionSource is a directory, i.e.
                  this is the name of the folder in the <profileDir>/extensions/<extensionID>
   """
   def installExtension(self, extensionSource, profileDir, extensionID = None):
     if not os.path.isdir(profileDir):
       self.log.info("INFO | automation.py | Cannot install extension, invalid profileDir at: %s", profileDir)
       return
 
-    extnsdir = os.path.join(profileDir, "extensions")
+    extnsdir = os.path.join(profileDir, "extensions", "staged")
 
     if os.path.isfile(extensionSource):
       # Copy extension xpi directly.
       # "destination file is created or overwritten".
       shutil.copy2(extensionSource, extnsdir)
     elif os.path.isdir(extensionSource):
       if extensionID == None:
         self.log.info("INFO | automation.py | Cannot install extension, missing extensionID")
--- a/testing/mochitest/runtests.py
+++ b/testing/mochitest/runtests.py
@@ -802,17 +802,17 @@ overlay chrome://navigator/content/navig
   def installChromeJar(self, jarDirName, chrome, options):
     """
       copy mochijar directory to profile as an extension so we have chrome://mochikit for all harness code
     """
     self.automation.installExtension(os.path.join(self.SCRIPT_DIRECTORY, jarDirName), \
                                      options.profilePath, "mochikit@mozilla.org")
 
     # Write chrome.manifest.
-    with open(os.path.join(options.profilePath, "extensions", "mochikit@mozilla.org", "chrome.manifest"), "a") as mfile:
+    with open(os.path.join(options.profilePath, "extensions", "staged", "mochikit@mozilla.org", "chrome.manifest"), "a") as mfile:
       mfile.write(chrome)
 
   def copyTestsJarToProfile(self, options):
     """ copy tests.jar to the profile directory so we can auto register it in the .xul harness """
     testsJarFile = os.path.join(self.SCRIPT_DIRECTORY, "tests.jar")
     if not os.path.isfile(testsJarFile):
       return False
 
--- a/toolkit/mozapps/extensions/LightweightThemeManager.jsm
+++ b/toolkit/mozapps/extensions/LightweightThemeManager.jsm
@@ -551,16 +551,20 @@ AddonWrapper.prototype = {
   get isPlatformCompatible() {
     return true;
   },
 
   get scope() {
     return AddonManager.SCOPE_PROFILE;
   },
 
+  get foreignInstall() {
+    return false;
+  },
+
   // Lightweight themes are always compatible
   isCompatibleWith: function(appVersion, platformVersion) {
     return true;
   },
 
   // Lightweight themes are always securely updated
   get providesUpdatesSecurely() {
     return true;
--- a/toolkit/mozapps/extensions/PluginProvider.jsm
+++ b/toolkit/mozapps/extensions/PluginProvider.jsm
@@ -334,16 +334,20 @@ PluginWrapper.prototype = {
   get isPlatformCompatible() {
     return true;
   },
 
   get providesUpdatesSecurely() {
     return true;
   },
 
+  get foreignInstall() {
+    return true;
+  },
+
   isCompatibleWith: function(aAppVerison, aPlatformVersion) {
     return true;
   },
 
   findUpdates: function(aListener, aReason, aAppVersion, aPlatformVersion) {
     if ("onNoCompatibilityUpdateAvailable" in aListener)
       aListener.onNoCompatibilityUpdateAvailable(this);
     if ("onNoUpdateAvailable" in aListener)
--- a/toolkit/mozapps/extensions/XPIProvider.jsm
+++ b/toolkit/mozapps/extensions/XPIProvider.jsm
@@ -117,17 +117,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                       = 5;
+const DB_SCHEMA                       = 6;
 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 +141,17 @@ 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"];
+                            "softDisabled", "foreignInstall"];
 
 const BOOTSTRAP_REASONS = {
   APP_STARTUP     : 1,
   APP_SHUTDOWN    : 2,
   ADDON_ENABLE    : 3,
   ADDON_DISABLE   : 4,
   ADDON_INSTALL   : 5,
   ADDON_UNINSTALL : 6,
@@ -2542,20 +2542,26 @@ var XPIProvider = {
 
       let newAddon = null;
       // Check the updated manifests lists for the install location, If there
       // is no manifest for the add-on ID then newAddon will be undefined
       if (aInstallLocation.name in aManifests)
         newAddon = aManifests[aInstallLocation.name][aId];
 
       try {
-        // Otherwise load the manifest from the add-on
+        // Otherwise the add-on has appeared in the install location.
         if (!newAddon) {
+          // Load the manifest from the add-on.
           let file = aInstallLocation.getLocationForID(aId);
           newAddon = loadManifestFromFile(file);
+
+          // The default theme is never a foreign install
+          if (newAddon.type != "theme" || newAddon.internalName != XPIProvider.defaultSkin)
+            newAddon.foreignInstall = true;
+
         }
         // The add-on in the manifest should match the add-on ID.
         if (newAddon.id != aId)
           throw new Error("Incorrect id in install manifest");
       }
       catch (e) {
         WARN("Add-on is invalid", e);
 
@@ -2569,19 +2575,20 @@ var XPIProvider = {
       }
 
       // Update the AddonInternal properties.
       newAddon._installLocation = aInstallLocation;
       newAddon.visible = !(newAddon.id in visibleAddons);
       newAddon.installDate = aAddonState.mtime;
       newAddon.updateDate = aAddonState.mtime;
 
-      // Check if the add-on is in a scope where add-ons should install disabled
+      // Check if the add-on is a foreign install and is in a scope where
+      // add-ons that were dropped in should default to disabled.
       let disablingScopes = Prefs.getIntPref(PREF_EM_AUTO_DISABLED_SCOPES, 0);
-      if (aInstallLocation.scope & disablingScopes)
+      if (newAddon.foreignInstall && aInstallLocation.scope & disablingScopes)
         newAddon.userDisabled = true;
 
       // If there is migration data then apply it.
       if (aMigrateData) {
         LOG("Migrating data from old database");
         // A theme's disabled state is determined by the selected theme
         // preference which is read in loadManifestFromRDF
         if (newAddon.type != "theme")
@@ -2591,16 +2598,18 @@ var XPIProvider = {
         if ("softDisabled" in aMigrateData)
           newAddon.softDisabled = aMigrateData.softDisabled;
         if ("applyBackgroundUpdates" in aMigrateData)
           newAddon.applyBackgroundUpdates = aMigrateData.applyBackgroundUpdates;
         if ("sourceURI" in aMigrateData)
           newAddon.sourceURI = aMigrateData.sourceURI;
         if ("releaseNotesURI" in aMigrateData)
           newAddon.releaseNotesURI = aMigrateData.releaseNotesURI;
+        if ("foreignInstall" in aMigrateData)
+          newAddon.foreignInstall = aMigrateData.foreignInstall;
 
         // Some properties should only be migrated if the add-on hasn't changed.
         // The version property isn't a perfect check for this but covers the
         // vast majority of cases.
         if (aMigrateData.version == newAddon.version &&
             "targetApplications" in aMigrateData) {
           LOG("Migrating compatibility info");
           newAddon.applyCompatibilityUpdate(aMigrateData, true);
@@ -3822,17 +3831,18 @@ 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";
+                     "skinnable, size, sourceURI, releaseNotesURI, softDisabled, " +
+                     "foreignInstall";
 
 /**
  * A helper function to log an SQL error.
  *
  * @param  aError
  *         The storage error code associated with the error
  * @param  aErrorString
  *         An error message
@@ -3967,17 +3977,18 @@ 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)",
+                            ":size, :sourceURI, :releaseNotesURI, :softDisabled, " +
+                            ":foreignInstall)",
     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 " +
@@ -4335,16 +4346,19 @@ var XPIDatabase = {
     // Attempt to migrate data from a different (even future!) version of the
     // database
     try {
       // Build a list of sql statements that might recover useful data from this
       // and future versions of the schema
       var sql = [];
       sql.push("SELECT internal_id, id, location, userDisabled, " +
                "softDisabled, installDate, version, applyBackgroundUpdates, " +
+               "sourceURI, releaseNotesURI, foreignInstall FROM addon");
+      sql.push("SELECT internal_id, id, location, userDisabled, " +
+               "softDisabled, installDate, version, applyBackgroundUpdates, " +
                "sourceURI, releaseNotesURI FROM addon");
       sql.push("SELECT internal_id, id, location, userDisabled, " +
                "installDate, version, applyBackgroundUpdates, " +
                "sourceURI, releaseNotesURI FROM addon");
       sql.push("SELECT internal_id, id, location, userDisabled, installDate, " +
                "version FROM addon");
 
       var stmt = null;
@@ -4375,16 +4389,18 @@ var XPIDatabase = {
         if ("softDisabled" in row)
           migrateData[row.location][row.id].softDisabled = row.softDisabled == 1;
         if ("applyBackgroundUpdates" in row)
           migrateData[row.location][row.id].applyBackgroundUpdates = row.applyBackgroundUpdates == 1;
         if ("sourceURI" in row)
           migrateData[row.location][row.id].sourceURI = row.sourceURI;
         if ("releaseNotesURI" in row)
           migrateData[row.location][row.id].releaseNotesURI = row.releaseNotesURI;
+        if ("foreignInstall" in row)
+          migrateData[row.location][row.id].foreignInstall = row.foreignInstall;
       }
 
       var taStmt = this.connection.createStatement("SELECT id, minVersion, " +
                                                    "maxVersion FROM " +
                                                    "targetApplication WHERE " +
                                                    "addon_internal_id=:internal_id");
 
       for (let location in migrateData) {
@@ -4495,16 +4511,17 @@ var XPIDatabase = {
                                   "visible INTEGER, active INTEGER, " +
                                   "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, " +
                                   "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, " +
@@ -5168,16 +5185,17 @@ var XPIDatabase = {
                                                           aDescriptor) {
     this.beginTransaction();
 
     // Any errors in here should rollback the transaction
     try {
       this.removeAddonMetadata(aOldAddon);
       aNewAddon.installDate = aOldAddon.installDate;
       aNewAddon.applyBackgroundUpdates = aOldAddon.applyBackgroundUpdates;
+      aNewAddon.foreignInstall = aOldAddon.foreignInstall;
       aNewAddon.active = (aNewAddon.visible && !aNewAddon.userDisabled &&
                           !aNewAddon.appDisabled)
       this.addAddonMetadata(aNewAddon, aDescriptor);
       this.commitTransaction();
     }
     catch (e) {
       this.rollbackTransaction();
       throw e;
@@ -6779,16 +6797,17 @@ AddonInternal.prototype = {
   _selectedLocale: null,
   active: false,
   visible: false,
   userDisabled: false,
   appDisabled: false,
   softDisabled: false,
   sourceURI: null,
   releaseNotesURI: null,
+  foreignInstall: false,
 
   get selectedLocale() {
     if (this._selectedLocale)
       return this._selectedLocale;
     let locale = findClosestLocale(this.locales);
     this._selectedLocale = locale ? locale : this.defaultLocale;
     return this._selectedLocale;
   },
@@ -7051,17 +7070,17 @@ function AddonWrapper(aAddon) {
       return [repositoryAddon[aProp], true];
     }
 
     return [objValue, false];
   }
 
   ["id", "version", "type", "isCompatible", "isPlatformCompatible",
    "providesUpdatesSecurely", "blocklistState", "blocklistURL", "appDisabled",
-   "softDisabled", "skinnable", "size"].forEach(function(aProp) {
+   "softDisabled", "skinnable", "size", "foreignInstall"].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_migrate4_7/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>addon7@tests.mozilla.org</em:id>
+    <em:version>1.0</em:version>
+
+    <!-- Front End MetaData -->
+    <em:name>Test 7</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>
--- a/toolkit/mozapps/extensions/test/xpcshell/test_distribution.js
+++ b/toolkit/mozapps/extensions/test/xpcshell/test_distribution.js
@@ -84,16 +84,17 @@ function run_test_1() {
 
   startupManager();
 
   AddonManager.getAddonByID("addon1@tests.mozilla.org", function(a1) {
     do_check_neq(a1, null);
     do_check_eq(a1.version, "1.0");
     do_check_true(a1.isActive);
     do_check_eq(a1.scope, AddonManager.SCOPE_PROFILE);
+    do_check_false(a1.foreignInstall);
 
     run_test_2();
   });
 }
 
 // Tests that starting with a newer version in the distribution dir doesn't
 // install it yet
 function run_test_2() {
@@ -117,16 +118,17 @@ function run_test_2() {
 function run_test_3() {
   restartManager("2");
 
   AddonManager.getAddonByID("addon1@tests.mozilla.org", function(a1) {
     do_check_neq(a1, null);
     do_check_eq(a1.version, "2.0");
     do_check_true(a1.isActive);
     do_check_eq(a1.scope, AddonManager.SCOPE_PROFILE);
+    do_check_false(a1.foreignInstall);
 
     run_test_4();
   });
 }
 
 // Test that an app upgrade doesn't downgrade the extension
 function run_test_4() {
   setOldModificationTime();
--- a/toolkit/mozapps/extensions/test/xpcshell/test_install.js
+++ b/toolkit/mozapps/extensions/test/xpcshell/test_install.js
@@ -158,16 +158,17 @@ function check_test_1() {
           do_check_neq(a1, null);
           do_check_eq(a1.type, "extension");
           do_check_eq(a1.version, "1.0");
           do_check_eq(a1.name, "Test 1");
           do_check_true(isExtensionInAddonsList(profileDir, a1.id));
           do_check_true(do_get_addon("test_install1").exists());
           do_check_in_crash_annotation(a1.id, a1.version);
           do_check_eq(a1.size, ADDON1_SIZE);
+          do_check_false(a1.foreignInstall);
 
           do_check_eq(a1.sourceURI.spec,
                       Services.io.newFileURI(do_get_addon("test_install1")).spec);
           let difference = a1.installDate.getTime() - updateDate;
           if (Math.abs(difference) > MAX_TIME_DIFFERENCE)
             do_throw("Add-on install time was out by " + difference + "ms");
 
           difference = a1.updateDate.getTime() - updateDate;
@@ -377,16 +378,17 @@ function check_test_5(install) {
           do_check_eq(a2.version, "3.0");
           do_check_eq(a2.name, "Real Test 3");
           do_check_true(a2.isActive);
           do_check_true(isExtensionInAddonsList(profileDir, a2.id));
           do_check_true(do_get_addon("test_install2_2").exists());
           do_check_in_crash_annotation(a2.id, a2.version);
           do_check_eq(a2.sourceURI.spec,
                       "http://localhost:4444/addons/test_install2_2.xpi");
+          do_check_false(a2.foreignInstall);
 
           do_check_eq(a2.installDate.getTime(), gInstallDate);
           // Update date should be later (or the same if this test is too fast)
           do_check_true(a2.installDate <= a2.updateDate);
 
           a2.uninstall();
           restartManager();
 
--- a/toolkit/mozapps/extensions/test/xpcshell/test_migrate4.js
+++ b/toolkit/mozapps/extensions/test/xpcshell/test_migrate4.js
@@ -106,39 +106,41 @@ function prepare_profile() {
                                "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, aInstall) {
-        completeAllInstalls([aInstall], function() {
-          restartManager();
+      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;
+            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();
+              a5.findUpdates({
+                onUpdateFinished: function() {
+                  shutdownManager();
 
-                perform_migration();
-              }
-            }, AddonManager.UPDATE_WHEN_USER_REQUESTED);
+                  perform_migration();
+                }
+              }, AddonManager.UPDATE_WHEN_USER_REQUESTED);
+            });
           });
-        });
+        }, "application/x-xpinstall");
       }
     }, AddonManager.UPDATE_WHEN_USER_REQUESTED);
   });
 }
 
 function perform_migration() {
   let dbfile = gProfD.clone();
   dbfile.append("extensions.sqlite");
@@ -161,62 +163,80 @@ function test_results() {
   check_startup_changes("disabled", []);
   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"],
-                               function([a1, a2, a3, a4, a5, a6]) {
+                               "addon6@tests.mozilla.org",
+                               "addon7@tests.mozilla.org"],
+                               function([a1, a2, a3, a4, a5, a6, a7]) {
     // 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);
 
     // 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);
 
     // 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);
 
     // 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);
 
     // 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);
 
     // 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");
+
+    // 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);
     testserver.stop(do_test_finished);
   });
 }
 
 function run_test() {
   do_test_pending();
 
   prepare_profile();
--- a/toolkit/mozapps/extensions/test/xpcshell/test_startup.js
+++ b/toolkit/mozapps/extensions/test/xpcshell/test_startup.js
@@ -197,38 +197,41 @@ function run_test_1() {
     do_check_eq(a1.version, "1.0");
     do_check_eq(a1.name, "Test 1");
     do_check_true(isExtensionInAddonsList(profileDir, a1.id));
     do_check_true(hasFlag(a1.permissions, AddonManager.PERM_CAN_UNINSTALL));
     do_check_true(hasFlag(a1.permissions, AddonManager.PERM_CAN_UPGRADE));
     do_check_in_crash_annotation(addon1.id, addon1.version);
     do_check_eq(a1.scope, AddonManager.SCOPE_PROFILE);
     do_check_eq(a1.sourceURI, null);
+    do_check_true(a1.foreignInstall);
 
     do_check_neq(a2, null);
     do_check_eq(a2.id, "addon2@tests.mozilla.org");
     do_check_eq(a2.version, "2.0");
     do_check_eq(a2.name, "Test 2");
     do_check_true(isExtensionInAddonsList(profileDir, a2.id));
     do_check_true(hasFlag(a2.permissions, AddonManager.PERM_CAN_UNINSTALL));
     do_check_true(hasFlag(a2.permissions, AddonManager.PERM_CAN_UPGRADE));
     do_check_in_crash_annotation(addon2.id, addon2.version);
     do_check_eq(a2.scope, AddonManager.SCOPE_PROFILE);
     do_check_eq(a2.sourceURI, null);
+    do_check_true(a2.foreignInstall);
 
     do_check_neq(a3, null);
     do_check_eq(a3.id, "addon3@tests.mozilla.org");
     do_check_eq(a3.version, "3.0");
     do_check_eq(a3.name, "Test 3");
     do_check_true(isExtensionInAddonsList(profileDir, a3.id));
     do_check_true(hasFlag(a3.permissions, AddonManager.PERM_CAN_UNINSTALL));
     do_check_true(hasFlag(a3.permissions, AddonManager.PERM_CAN_UPGRADE));
     do_check_in_crash_annotation(addon3.id, addon3.version);
     do_check_eq(a3.scope, AddonManager.SCOPE_PROFILE);
     do_check_eq(a3.sourceURI, null);
+    do_check_true(a3.foreignInstall);
 
     do_check_eq(a4, null);
     do_check_false(isExtensionInAddonsList(profileDir, "addon4@tests.mozilla.org"));
     dest = profileDir.clone();
     dest.append(do_get_expected_addon_name("addon4@tests.mozilla.org"));
     do_check_false(dest.exists());
 
     do_check_eq(a5, null);
@@ -292,27 +295,29 @@ function run_test_2() {
     do_check_eq(a1.id, "addon1@tests.mozilla.org");
     do_check_eq(a1.version, "1.0");
     do_check_true(isExtensionInAddonsList(profileDir, a1.id));
     do_check_false(isExtensionInAddonsList(userDir, a1.id));
     do_check_true(hasFlag(a1.permissions, AddonManager.PERM_CAN_UNINSTALL));
     do_check_true(hasFlag(a1.permissions, AddonManager.PERM_CAN_UPGRADE));
     do_check_in_crash_annotation(addon1.id, a1.version);
     do_check_eq(a1.scope, AddonManager.SCOPE_PROFILE);
+    do_check_true(a1.foreignInstall);
 
     do_check_neq(a2, null);
     do_check_eq(a2.id, "addon2@tests.mozilla.org");
     do_check_eq(a2.version, "2.1");
     do_check_true(isExtensionInAddonsList(profileDir, a2.id));
     do_check_false(isExtensionInAddonsList(userDir, a2.id));
     do_check_false(isExtensionInAddonsList(globalDir, a2.id));
     do_check_true(hasFlag(a2.permissions, AddonManager.PERM_CAN_UNINSTALL));
     do_check_true(hasFlag(a2.permissions, AddonManager.PERM_CAN_UPGRADE));
     do_check_in_crash_annotation(addon2.id, a2.version);
     do_check_eq(a2.scope, AddonManager.SCOPE_PROFILE);
+    do_check_true(a2.foreignInstall);
 
     do_check_eq(a3, null);
     do_check_false(isExtensionInAddonsList(profileDir, "addon3@tests.mozilla.org"));
     do_check_not_in_crash_annotation(addon3.id, addon3.version);
 
     do_check_eq(a4, null);
     do_check_false(isExtensionInAddonsList(profileDir, "addon4@tests.mozilla.org"));
 
--- a/toolkit/mozapps/extensions/test/xpcshell/test_theme.js
+++ b/toolkit/mozapps/extensions/test/xpcshell/test_theme.js
@@ -89,34 +89,37 @@ function run_test() {
   AddonManager.addInstallListener(InstallListener);
 
   AddonManager.getAddonsByIDs(["default@tests.mozilla.org",
                                "theme1@tests.mozilla.org",
                                "theme2@tests.mozilla.org"],
                                function([d, t1, t2]) {
     do_check_neq(d, null);
     do_check_false(d.skinnable);
+    do_check_false(d.foreignInstall);
 
     do_check_neq(t1, null);
     do_check_false(t1.userDisabled);
     do_check_false(t1.appDisabled);
     do_check_true(t1.isActive);
     do_check_true(t1.skinnable);
+    do_check_true(t1.foreignInstall);
     do_check_eq(t1.screenshots, null);
     do_check_true(isThemeInAddonsList(profileDir, t1.id));
     do_check_false(hasFlag(t1.permissions, AddonManager.PERM_CAN_DISABLE));
     do_check_false(hasFlag(t1.permissions, AddonManager.PERM_CAN_ENABLE));
     do_check_eq(t1.operationsRequiringRestart, AddonManager.OP_NEEDS_RESTART_UNINSTALL |
                                                AddonManager.OP_NEEDS_RESTART_DISABLE);
 
     do_check_neq(t2, null);
     do_check_true(t2.userDisabled);
     do_check_false(t2.appDisabled);
     do_check_false(t2.isActive);
     do_check_false(t2.skinnable);
+    do_check_true(t2.foreignInstall);
     do_check_eq(t2.screenshots, null);
     do_check_false(isThemeInAddonsList(profileDir, t2.id));
     do_check_false(hasFlag(t2.permissions, AddonManager.PERM_CAN_DISABLE));
     do_check_true(hasFlag(t2.permissions, AddonManager.PERM_CAN_ENABLE));
     do_check_eq(t2.operationsRequiringRestart, AddonManager.OP_NEEDS_RESTART_ENABLE);
 
     run_test_1();
   });
--- a/toolkit/mozapps/extensions/test/xpcshell/test_update.js
+++ b/toolkit/mozapps/extensions/test/xpcshell/test_update.js
@@ -85,16 +85,17 @@ function end_test() {
 
 // Verify that an update is available and can be installed.
 function run_test_1() {
   AddonManager.getAddonByID("addon1@tests.mozilla.org", function(a1) {
     do_check_neq(a1, null);
     do_check_eq(a1.version, "1.0");
     do_check_eq(a1.applyBackgroundUpdates, AddonManager.AUTOUPDATE_DEFAULT);
     do_check_eq(a1.releaseNotesURI, null);
+    do_check_true(a1.foreignInstall);
 
     a1.applyBackgroundUpdates = AddonManager.AUTOUPDATE_DEFAULT;
 
     prepare_test({
       "addon1@tests.mozilla.org": [
         ["onPropertyChanged", ["applyBackgroundUpdates"]]
       ]
     });
@@ -214,16 +215,17 @@ function check_test_2() {
     do_check_true(isExtensionInAddonsList(profileDir, olda1.id));
 
     AddonManager.getAddonByID("addon1@tests.mozilla.org", function(a1) {
       do_check_neq(a1, null);
       do_check_eq(a1.version, "2.0");
       do_check_true(isExtensionInAddonsList(profileDir, a1.id));
       do_check_eq(a1.applyBackgroundUpdates, AddonManager.AUTOUPDATE_DISABLE);
       do_check_eq(a1.releaseNotesURI.spec, "http://example.com/updateInfo.xhtml");
+      do_check_true(a1.foreignInstall);
 
       a1.uninstall();
       restartManager();
 
       run_test_3();
     });
   });
 }