Bug 553017: Make revealing and hiding bootstrapped add-ons in non-profile install locations work correctly. r=robstrong, a=blocks-final

This commit is contained in:
Dave Townsend 2010-11-05 09:20:14 -07:00
parent e8a5ffb557
commit 666a25ba40
5 changed files with 384 additions and 23 deletions

View File

@ -1853,13 +1853,22 @@ var XPIProvider = {
XPIDatabase.updateAddonMetadata(aOldAddon, newAddon, aAddonState.descriptor);
if (newAddon.visible) {
visibleAddons[newAddon.id] = newAddon;
// If the old version was active and wasn't bootstrapped or the new
// version will be active and isn't bootstrapped then we must force a
// restart
if ((aOldAddon.active && !aOldAddon.bootstrap) ||
(newAddon.active && !newAddon.bootstrap)) {
return true;
// If the new add-on is bootstrapped and active then call its install method
if (newAddon.active && newAddon.bootstrap) {
let installReason = Services.vc.compare(aOldAddon.version, newAddon.version) < 0 ?
BOOTSTRAP_REASONS.ADDON_UPGRADE :
BOOTSTRAP_REASONS.ADDON_DOWNGRADE;
let file = Cc["@mozilla.org/file/local;1"].createInstance(Ci.nsILocalFile);
file.persistentDescriptor = aAddonState.descriptor;
XPIProvider.callBootstrapMethod(newAddon.id, newAddon.version, file,
"install", installReason);
return false;
}
// Otherwise the caches will need to be invalidated
return true;
}
return false;
@ -1892,16 +1901,23 @@ var XPIProvider = {
if (!aOldAddon.visible) {
XPIDatabase.makeAddonVisible(aOldAddon);
// If the add-on is bootstrappable and it should be active then
// mark it as active and add it to the list to be activated.
if (aOldAddon.bootstrap && !aOldAddon.appDisabled &&
!aOldAddon.userDisabled) {
aOldAddon.active = true;
XPIDatabase.updateAddonActive(aOldAddon);
XPIProvider.bootstrappedAddons[aOldAddon.id] = {
version: aOldAddon.version,
descriptor: aAddonState.descriptor
};
if (aOldAddon.bootstrap) {
// The add-on is bootstrappable so call its install script
let file = Cc["@mozilla.org/file/local;1"].createInstance(Ci.nsILocalFile);
file.persistentDescriptor = aAddonState.descriptor;
XPIProvider.callBootstrapMethod(aOldAddon.id, aOldAddon.version, file,
"install",
BOOTSTRAP_REASONS.ADDON_INSTALL);
// If it should be active then mark it as active otherwise unload
// its scope
if (!aOldAddon.appDisabled && !aOldAddon.userDisabled) {
aOldAddon.active = true;
XPIDatabase.updateAddonActive(aOldAddon);
}
else {
XPIProvider.unloadBootstrapScope(newAddon.id);
}
}
else {
// Otherwise a restart is necessary
@ -1988,7 +2004,11 @@ var XPIProvider = {
if (!aOldAddon.bootstrap)
return true;
XPIProvider.unloadBootstrapScope(aOldAddon.id);
// If this is the currently active bootstrapped add-on for this ID then
// remove it from the list.
if (aOldAddon.id in XPIProvider.bootstrappedAddons &&
XPIProvider.bootstrappedAddons[aOldAddon.id].descriptor == aOldAddon._descriptor)
XPIProvider.unloadBootstrapScope(aOldAddon.id);
}
return false;
@ -2083,6 +2103,26 @@ var XPIProvider = {
XPIProvider.allAppGlobal = false;
visibleAddons[newAddon.id] = newAddon;
let installReason = BOOTSTRAP_REASONS.ADDON_INSTALL;
// If we're hiding a bootstrapped add-on then call its uninstall method
if (newAddon.id in XPIProvider.bootstrappedAddons) {
let oldBootstrap = XPIProvider.bootstrappedAddons[newAddon.id];
installReason = Services.vc.compare(oldBootstrap.version, newAddon.version) < 0 ?
BOOTSTRAP_REASONS.ADDON_UPGRADE :
BOOTSTRAP_REASONS.ADDON_DOWNGRADE;
let oldAddonFile = Cc["@mozilla.org/file/local;1"].
createInstance(Ci.nsILocalFile);
oldAddonFile.persistentDescriptor = oldBootstrap.descriptor;
XPIProvider.callBootstrapMethod(newAddon.id, oldBootstrap.version,
oldAddonFile, "uninstall",
installReason);
XPIProvider.unloadBootstrapScope(newAddon.id);
}
if (!newAddon.bootstrap)
return true;
@ -2090,8 +2130,7 @@ var XPIProvider = {
let file = Cc["@mozilla.org/file/local;1"].createInstance(Ci.nsILocalFile);
file.persistentDescriptor = aAddonState.descriptor;
XPIProvider.callBootstrapMethod(newAddon.id, newAddon.version, file,
"install",
BOOTSTRAP_REASONS.ADDON_INSTALL);
"install", installReason);
if (!newAddon.active)
XPIProvider.unloadBootstrapScope(newAddon.id);
}
@ -3037,7 +3076,53 @@ var XPIProvider = {
aAddon._installLocation.uninstallAddon(aAddon.id);
XPIDatabase.removeAddonMetadata(aAddon);
AddonManagerPrivate.callAddonListeners("onUninstalled", wrapper);
// TODO reveal hidden add-ons (bug 557710)
// Reveal the highest priority add-on with the same ID
function revealAddon(aAddon) {
XPIDatabase.makeAddonVisible(aAddon);
let wrappedAddon = createWrapper(aAddon);
AddonManagerPrivate.callAddonListeners("onInstalling", wrappedAddon, false);
if (!aAddon.userDisabled && !aAddon.appDisabled &&
!XPIProvider.enableRequiresRestart(aAddon)) {
aAddon.active = true;
XPIDatabase.updateAddonActive(aAddon);
}
if (aAddon.bootstrap) {
let file = aAddon._installLocation.getLocationForID(aAddon.id);
XPIProvider.callBootstrapMethod(aAddon.id, aAddon.version, file,
"install", BOOTSTRAP_REASONS.ADDON_INSTALL);
if (aAddon.active) {
XPIProvider.callBootstrapMethod(aAddon.id, aAddon.version, file,
"startup", BOOTSTRAP_REASONS.ADDON_INSTALL);
}
else {
XPIProvider.unloadBootstrapScope(aAddon.id);
}
}
// We always send onInstalled even if a restart is required to enable
// the revealed add-on
AddonManagerPrivate.callAddonListeners("onInstalled", wrappedAddon);
}
function checkInstallLocation(aPos) {
if (aPos < 0)
return;
let location = XPIProvider.installLocations[aPos];
XPIDatabase.getAddonInLocation(aAddon.id, location.name, function(aNewAddon) {
if (aNewAddon)
revealAddon(aNewAddon);
else
checkInstallLocation(aPos - 1);
})
}
checkInstallLocation(this.installLocations.length - 1);
}
// Notify any other providers that a new theme has been enabled

View File

@ -2,6 +2,7 @@ Components.utils.import("resource://gre/modules/Services.jsm");
function install(data, reason) {
Services.prefs.setIntPref("bootstraptest.installed_version", 1);
Services.prefs.setIntPref("bootstraptest.install_reason", reason);
}
function startup(data, reason) {
@ -16,4 +17,5 @@ function shutdown(data, reason) {
function uninstall(data, reason) {
Services.prefs.setIntPref("bootstraptest.installed_version", 0);
Services.prefs.setIntPref("bootstraptest.uninstall_reason", reason);
}

View File

@ -2,6 +2,7 @@ Components.utils.import("resource://gre/modules/Services.jsm");
function install(data, reason) {
Services.prefs.setIntPref("bootstraptest.installed_version", 2);
Services.prefs.setIntPref("bootstraptest.install_reason", reason);
}
function startup(data, reason) {
@ -16,4 +17,5 @@ function shutdown(data, reason) {
function uninstall(data, reason) {
Services.prefs.setIntPref("bootstraptest.installed_version", 0);
Services.prefs.setIntPref("bootstraptest.uninstall_reason", reason);
}

View File

@ -2,6 +2,7 @@ Components.utils.import("resource://gre/modules/Services.jsm");
function install(data, reason) {
Services.prefs.setIntPref("bootstraptest.installed_version", 3);
Services.prefs.setIntPref("bootstraptest.install_reason", reason);
}
function startup(data, reason) {
@ -16,4 +17,5 @@ function shutdown(data, reason) {
function uninstall(data, reason) {
Services.prefs.setIntPref("bootstraptest.installed_version", 0);
Services.prefs.setIntPref("bootstraptest.uninstall_reason", reason);
}

View File

@ -14,10 +14,27 @@ const ADDON_DOWNGRADE = 8;
// This verifies that bootstrappable add-ons can be used without restarts.
Components.utils.import("resource://gre/modules/Services.jsm");
Services.prefs.setIntPref("bootstraptest.version", 0);
// Enable loading extensions from the user scopes
Services.prefs.setIntPref("extensions.enabledScopes",
AddonManager.SCOPE_PROFILE + AddonManager.SCOPE_USER);
createAppInfo("xpcshell@tests.mozilla.org", "XPCShell", "1", "1.9.2");
const profileDir = gProfD.clone();
profileDir.append("extensions");
const userExtDir = gProfD.clone();
userExtDir.append("extensions2");
userExtDir.append(gAppInfo.ID);
registerDirectory("XREUSysExt", userExtDir.parent);
function resetPrefs() {
Services.prefs.setIntPref("bootstraptest.active_version", -1);
Services.prefs.setIntPref("bootstraptest.installed_version", -1);
Services.prefs.setIntPref("bootstraptest.startup_reason", -1);
Services.prefs.setIntPref("bootstraptest.shutdown_reason", -1);
Services.prefs.setIntPref("bootstraptest.install_reason", -1);
Services.prefs.setIntPref("bootstraptest.uninstall_reason", -1);
}
function getActiveVersion() {
return Services.prefs.getIntPref("bootstraptest.active_version");
@ -35,9 +52,18 @@ function getShutdownReason() {
return Services.prefs.getIntPref("bootstraptest.shutdown_reason");
}
function getInstallReason() {
return Services.prefs.getIntPref("bootstraptest.install_reason");
}
function getUninstallReason() {
return Services.prefs.getIntPref("bootstraptest.uninstall_reason");
}
function run_test() {
do_test_pending();
createAppInfo("xpcshell@tests.mozilla.org", "XPCShell", "1", "1.9.2");
resetPrefs();
startupManager();
@ -746,8 +772,252 @@ function run_test_16() {
do_check_eq(getInstalledVersion(), 1);
do_check_eq(getActiveVersion(), 1);
do_test_finished();
AddonManager.getAddonByID("bootstrap1@tests.mozilla.org", function(b1) {
b1.uninstall();
run_test_17();
});
});
});
});
}
// Check that a bootstrapped extension in a non-profile location is loaded
function run_test_17() {
shutdownManager();
let dir = userExtDir.clone();
dir.append("bootstrap1@tests.mozilla.org");
dir.create(AM_Ci.nsIFile.DIRECTORY_TYPE, 0755);
let zip = AM_Cc["@mozilla.org/libjar/zip-reader;1"].
createInstance(AM_Ci.nsIZipReader);
zip.open(do_get_addon("test_bootstrap1_1"));
dir.append("install.rdf");
zip.extract("install.rdf", dir);
dir = dir.parent;
dir.append("bootstrap.js");
zip.extract("bootstrap.js", dir);
zip.close();
resetPrefs();
startupManager();
AddonManager.getAddonByID("bootstrap1@tests.mozilla.org", function(b1) {
// Should have installed and started
do_check_eq(getInstalledVersion(), 1);
do_check_eq(getActiveVersion(), 1);
do_check_neq(b1, null);
do_check_eq(b1.version, "1.0");
do_check_true(b1.isActive);
run_test_18();
});
}
// Check that installing a new bootstrapped extension in the profile replaces
// the existing one
function run_test_18() {
resetPrefs();
installAllFiles([do_get_addon("test_bootstrap1_2")], function() {
AddonManager.getAddonByID("bootstrap1@tests.mozilla.org", function(b1) {
// Should have installed and started
do_check_eq(getInstalledVersion(), 2);
do_check_eq(getActiveVersion(), 2);
do_check_neq(b1, null);
do_check_eq(b1.version, "2.0");
do_check_true(b1.isActive);
do_check_eq(getShutdownReason(), ADDON_UPGRADE);
do_check_eq(getUninstallReason(), ADDON_UPGRADE);
do_check_eq(getInstallReason(), ADDON_UPGRADE);
do_check_eq(getStartupReason(), ADDON_UPGRADE);
run_test_19();
});
});
}
// Check that uninstalling the profile version reveals the non-profile one
function run_test_19() {
resetPrefs();
AddonManager.getAddonByID("bootstrap1@tests.mozilla.org", function(b1) {
// The revealed add-on gets activated asynchronously
prepare_test({
"bootstrap1@tests.mozilla.org": [
["onUninstalling", false],
"onUninstalled",
["onInstalling", false],
"onInstalled"
]
}, [], check_test_19);
b1.uninstall();
});
}
function check_test_19() {
AddonManager.getAddonByID("bootstrap1@tests.mozilla.org", function(b1) {
// Should have reverted to the older version
do_check_eq(getInstalledVersion(), 1);
do_check_eq(getActiveVersion(), 1);
do_check_neq(b1, null);
do_check_eq(b1.version, "1.0");
do_check_true(b1.isActive);
// TODO these reasons really should be ADDON_DOWNGRADE (bug 607818)
do_check_eq(getShutdownReason(), ADDON_UNINSTALL);
do_check_eq(getUninstallReason(), ADDON_UNINSTALL);
do_check_eq(getInstallReason(), ADDON_INSTALL);
do_check_eq(getStartupReason(), ADDON_INSTALL);
run_test_20();
});
}
// Check that a new profile extension detected at startup replaces the non-profile
// one
function run_test_20() {
resetPrefs();
shutdownManager();
let dir = profileDir.clone();
dir.append("bootstrap1@tests.mozilla.org");
dir.create(AM_Ci.nsIFile.DIRECTORY_TYPE, 0755);
let zip = AM_Cc["@mozilla.org/libjar/zip-reader;1"].
createInstance(AM_Ci.nsIZipReader);
zip.open(do_get_addon("test_bootstrap1_2"));
dir.append("install.rdf");
zip.extract("install.rdf", dir);
dir = dir.parent;
dir.append("bootstrap.js");
zip.extract("bootstrap.js", dir);
zip.close();
startupManager();
AddonManager.getAddonByID("bootstrap1@tests.mozilla.org", function(b1) {
// Should have installed and started
do_check_eq(getInstalledVersion(), 2);
do_check_eq(getActiveVersion(), 2);
do_check_neq(b1, null);
do_check_eq(b1.version, "2.0");
do_check_true(b1.isActive);
do_check_eq(getShutdownReason(), APP_SHUTDOWN);
do_check_eq(getUninstallReason(), ADDON_UPGRADE);
do_check_eq(getInstallReason(), ADDON_UPGRADE);
do_check_eq(getStartupReason(), APP_STARTUP);
run_test_21();
});
}
// Check that a detected removal reveals the non-profile one
function run_test_21() {
resetPrefs();
shutdownManager();
let dir = profileDir.clone();
dir.append("bootstrap1@tests.mozilla.org");
dir.remove(true);
startupManager();
AddonManager.getAddonByID("bootstrap1@tests.mozilla.org", function(b1) {
// Should have installed and started
do_check_eq(getInstalledVersion(), 1);
do_check_eq(getActiveVersion(), 1);
do_check_neq(b1, null);
do_check_eq(b1.version, "1.0");
do_check_true(b1.isActive);
do_check_eq(getShutdownReason(), APP_SHUTDOWN);
// This won't be set as the bootstrap script was gone so we couldn't
// uninstall it properly
do_check_eq(getUninstallReason(), -1);
// TODO this reason should probably be ADDON_DOWNGRADE (bug 607818)
do_check_eq(getInstallReason(), ADDON_INSTALL);
do_check_eq(getStartupReason(), APP_STARTUP);
dir = userExtDir.clone();
dir.append("bootstrap1@tests.mozilla.org");
dir.remove(true);
restartManager();
run_test_22();
});
}
// Check that an upgrade from the filesystem is detected and applied correctly
function run_test_22() {
shutdownManager();
let dir = profileDir.clone();
dir.append("bootstrap1@tests.mozilla.org");
dir.create(AM_Ci.nsIFile.DIRECTORY_TYPE, 0755);
let zip = AM_Cc["@mozilla.org/libjar/zip-reader;1"].
createInstance(AM_Ci.nsIZipReader);
zip.open(do_get_addon("test_bootstrap1_1"));
dir.append("install.rdf");
zip.extract("install.rdf", dir);
dir = dir.parent;
dir.append("bootstrap.js");
zip.extract("bootstrap.js", dir);
zip.close();
// Make it look old so changes are detected
setExtensionModifiedTime(dir.parent, dir.parent.lastModifiedTime - 5000);
startupManager();
AddonManager.getAddonByID("bootstrap1@tests.mozilla.org", function(b1) {
// Should have installed and started
do_check_eq(getInstalledVersion(), 1);
do_check_eq(getActiveVersion(), 1);
do_check_neq(b1, null);
do_check_eq(b1.version, "1.0");
do_check_true(b1.isActive);
resetPrefs();
shutdownManager();
dir = dir.parent;
dir.remove(true);
dir.create(AM_Ci.nsIFile.DIRECTORY_TYPE, 0755);
let zip = AM_Cc["@mozilla.org/libjar/zip-reader;1"].
createInstance(AM_Ci.nsIZipReader);
zip.open(do_get_addon("test_bootstrap1_2"));
dir.append("install.rdf");
zip.extract("install.rdf", dir);
dir = dir.parent;
dir.append("bootstrap.js");
zip.extract("bootstrap.js", dir);
zip.close();
startupManager();
AddonManager.getAddonByID("bootstrap1@tests.mozilla.org", function(b1) {
// Should have installed and started
do_check_eq(getInstalledVersion(), 2);
do_check_eq(getActiveVersion(), 2);
do_check_neq(b1, null);
do_check_eq(b1.version, "2.0");
do_check_true(b1.isActive);
do_check_eq(getShutdownReason(), APP_SHUTDOWN);
// This won't be set as the bootstrap script was gone so we couldn't
// uninstall it properly
do_check_eq(getUninstallReason(), -1);
do_check_eq(getInstallReason(), ADDON_UPGRADE);
do_check_eq(getStartupReason(), APP_STARTUP);
do_test_finished();
});
});
}