Bug 555083: Add support for registry install locations. r=robstrong

This commit is contained in:
Dave Townsend 2010-04-08 12:28:10 -07:00
parent b81f3abe6a
commit ddc0c3c62c
4 changed files with 325 additions and 23 deletions

View File

@ -782,22 +782,53 @@ var XPIProvider = {
this.installLocations = [];
this.installLocationsByName = {};
// These must be in order of priority for processFileChanges etc. to work
[
[KEY_APP_GLOBAL, KEY_APPDIR, [DIR_EXTENSIONS], true],
[KEY_APP_SYSTEM_LOCAL, "XRESysLExtPD", [Services.appinfo.ID], true],
[KEY_APP_SYSTEM_SHARE, "XRESysSExtPD", [Services.appinfo.ID], true],
[KEY_APP_SYSTEM_USER, "XREUSysExt", [Services.appinfo.ID], true],
[KEY_APP_PROFILE, KEY_PROFILEDIR, [DIR_EXTENSIONS], false]
].forEach(function([name, key, paths, locked]) {
function addDirectoryInstallLocation(name, key, paths, locked) {
try {
let dir = FileUtils.getDir(key, paths);
let location = new DirectoryInstallLocation(name, dir, locked);
this.installLocations.push(location);
this.installLocationsByName[location.name] = location;
var dir = FileUtils.getDir(key, paths);
}
catch (e) { }
}, this);
catch (e) {
// Some directories aren't defined on some platforms, ignore them
LOG("Skipping unavailable install location " + name);
return;
}
try {
var location = new DirectoryInstallLocation(name, dir, locked);
}
catch (e) {
WARN("Failed to add directory install location " + name + " " + e);
return;
}
XPIProvider.installLocations.push(location);
XPIProvider.installLocationsByName[location.name] = location;
}
function addRegistryInstallLocation(name, rootkey) {
try {
var location = new WinRegInstallLocation(name, rootkey);
}
catch (e) {
WARN("Failed to add registry install location " + name + " " + e);
return;
}
XPIProvider.installLocations.push(location);
XPIProvider.installLocationsByName[location.name] = location;
}
let hasRegistry = ("nsIWindowsRegKey" in Ci);
// These must be in order of priority for processFileChanges etc. to work
if (hasRegistry)
addRegistryInstallLocation("winreg-app-global", Ci.nsIWindowsRegKey.ROOT_KEY_LOCAL_MACHINE);
addDirectoryInstallLocation(KEY_APP_SYSTEM_LOCAL, "XRESysLExtPD", [Services.appinfo.ID], true);
addDirectoryInstallLocation(KEY_APP_SYSTEM_SHARE, "XRESysSExtPD", [Services.appinfo.ID], true);
addDirectoryInstallLocation(KEY_APP_GLOBAL, KEY_APPDIR, [DIR_EXTENSIONS], true);
if (hasRegistry)
addRegistryInstallLocation("winreg-app-user", Ci.nsIWindowsRegKey.ROOT_KEY_CURRENT_USER);
addDirectoryInstallLocation(KEY_APP_SYSTEM_USER, "XREUSysExt", [Services.appinfo.ID], true);
addDirectoryInstallLocation(KEY_APP_PROFILE, KEY_PROFILEDIR, [DIR_EXTENSIONS], false);
this.defaultSkin = Prefs.getDefaultCharPref(PREF_GENERAL_SKINS_SELECTEDSKIN,
"classic/1.0");
@ -1092,7 +1123,10 @@ var XPIProvider = {
catch (e) {
WARN("Add-on is invalid: " + e);
XPIDatabase.removeAddonMetadata(oldAddon);
installLocation.uninstallAddon(oldAddon.id);
if (!installLocation.locked)
installLocation.uninstallAddon(oldAddon.id);
else
WARN("Could not uninstall invalid item from locked install location");
// If this was an active add-on then we must force a restart
if (oldAddon.active) {
if (oldAddon.type == "bootstrapped")
@ -1277,9 +1311,12 @@ var XPIProvider = {
catch (e) {
WARN("Add-on is invalid: " + e);
// Remove the invalid add-on from the install location, no restart will
// be necessary
installLocation.uninstallAddon(id);
// Remove the invalid add-on from the install location if the install
// location isn't locked, no restart will be necessary
if (!installLocation.locked)
installLocation.uninstallAddon(id);
else
WARN("Could not uninstall invalid item from locked install location");
return false;
}
@ -4582,11 +4619,13 @@ function WinRegInstallLocation(name, rootKey) {
// cases, we just leave ourselves in the empty state.
try {
key.open(this._rootKey, path, Ci.nsIWindowsRegKey.ACCESS_READ);
this._readAddons(key);
}
catch (e) { }
if (key)
key.close();
catch (e) {
return;
}
this._readAddons(key);
key.close();
}
WinRegInstallLocation.prototype = {
@ -4635,6 +4674,9 @@ WinRegInstallLocation.prototype = {
this._IDToDirMap[id] = dir;
this._DirToIDMap[dir.path] = id;
}
else {
WARN("Ignoring missing add-on in " + dir.path);
}
}
},

View File

@ -49,7 +49,7 @@ function createAppInfo(id, name, version, platformVersion) {
AM_Ci.nsICrashReporter,
AM_Ci.nsISupports])
};
var XULAppInfoFactory = {
createInstance: function (outer, iid) {
if (outer != null)
@ -528,6 +528,116 @@ function ensure_test_completed() {
if (gExpectedInstalls)
do_check_eq(gExpectedInstalls.length, 0);
}
if ("nsIWindowsRegKey" in AM_Ci) {
var MockRegistry = {
LOCAL_MACHINE: {},
CURRENT_USER: {},
setValue: function(root, path, name, value) {
switch (root) {
case AM_Ci.nsIWindowsRegKey.ROOT_KEY_LOCAL_MACHINE:
var rootKey = MockRegistry.LOCAL_MACHINE;
break
case AM_Ci.nsIWindowsRegKey.ROOT_KEY_CURRENT_USER:
rootKey = MockRegistry.CURRENT_USER;
break
}
if (!(path in rootKey)) {
rootKey[path] = [];
}
else {
for (let i = 0; i < rootKey[path].length; i++) {
if (rootKey[path][i].name == name) {
if (value === null)
rootKey[path].splice(i, 1);
else
rootKey[path][i].value = value;
return;
}
}
}
if (value === null)
return;
rootKey[path].push({
name: name,
value: value
});
}
};
/**
* This is a mock nsIWindowsRegistry implementation. It only implements the
* methods that the extension manager requires.
*/
function MockWindowsRegKey() {
}
MockWindowsRegKey.prototype = {
values: null,
// --- Overridden nsISupports interface functions ---
QueryInterface: XPCOMUtils.generateQI([AM_Ci.nsIWindowsRegKey]),
// --- Overridden nsIWindowsRegKey interface functions ---
open: function(aRootKey, aRelPath, aMode) {
switch (aRootKey) {
case AM_Ci.nsIWindowsRegKey.ROOT_KEY_LOCAL_MACHINE:
var rootKey = MockRegistry.LOCAL_MACHINE;
break
case AM_Ci.nsIWindowsRegKey.ROOT_KEY_CURRENT_USER:
rootKey = MockRegistry.CURRENT_USER;
break
}
if (!(aRelPath in rootKey))
rootKey[aRelPath] = [];
this.values = rootKey[aRelPath];
},
close: function() {
this.values = null;
},
get valueCount() {
if (!this.values)
throw Components.results.NS_ERROR_FAILURE;
return this.values.length;
},
getValueName: function(aIndex) {
if (!this.values || aIndex >= this.values.length)
throw Components.results.NS_ERROR_FAILURE;
return this.values[aIndex].name;
},
readStringValue: function(aName) {
for (let i = 0; i < this.values.length; i++) {
if (this.values[i].name == aName)
return this.values[i].value;
}
}
};
var WinRegFactory = {
createInstance: function(aOuter, aIid) {
if (aOuter != null)
throw Components.results.NS_ERROR_NO_AGGREGATION;
var key = new MockWindowsRegKey();
return key.QueryInterface(aIid);
}
};
var registrar = Components.manager.QueryInterface(AM_Ci.nsIComponentRegistrar);
registrar.registerFactory(Components.ID("{0478de5b-0f38-4edb-851d-4c99f1ed8eba}"),
"Mock Windows Registry Implementation",
"@mozilla.org/windows-registry-key;1", WinRegFactory);
}
// Get the profile directory for tests to use.
const gProfD = do_get_profile().QueryInterface(AM_Ci.nsILocalFile);

View File

@ -0,0 +1,147 @@
/* Any copyright is dedicated to the Public Domain.
* http://creativecommons.org/publicdomain/zero/1.0/
*/
// Tests that extensions installed through the registry work as expected
createAppInfo("xpcshell@tests.mozilla.org", "XPCShell", "1", "1.9.2");
var addon1 = {
id: "addon1@tests.mozilla.org",
version: "1.0",
name: "Test 1",
targetApplications: [{
id: "xpcshell@tests.mozilla.org",
minVersion: "1",
maxVersion: "1"
}]
};
var addon2 = {
id: "addon2@tests.mozilla.org",
version: "2.0",
name: "Test 2",
targetApplications: [{
id: "xpcshell@tests.mozilla.org",
minVersion: "1",
maxVersion: "2"
}]
};
const addon1Dir = gProfD.clone();
addon1Dir.append("addon1");
writeInstallRDFToDir(addon1, addon1Dir);
const addon2Dir = gProfD.clone();
addon2Dir.append("addon2");
writeInstallRDFToDir(addon2, addon2Dir);
function run_test() {
// This test only works where there is a registry.
if (!("nsIWindowsRegKey" in AM_Ci))
return;
do_test_pending();
run_test_1();
}
// Tests whether basic registry install works
function run_test_1() {
MockRegistry.setValue(AM_Ci.nsIWindowsRegKey.ROOT_KEY_LOCAL_MACHINE,
"SOFTWARE\\Mozilla\\XPCShell\\Extensions",
"addon1@tests.mozilla.org", addon1Dir.path);
MockRegistry.setValue(AM_Ci.nsIWindowsRegKey.ROOT_KEY_CURRENT_USER,
"SOFTWARE\\Mozilla\\XPCShell\\Extensions",
"addon2@tests.mozilla.org", addon2Dir.path);
startupManager(1);
AddonManager.getAddons(["addon1@tests.mozilla.org",
"addon2@tests.mozilla.org"], function([a1, a2]) {
do_check_neq(a1, null);
do_check_true(a1.isActive);
do_check_false(hasFlag(a1.permissions, AddonManager.PERM_CAN_UNINSTALL));
do_check_neq(a2, null);
do_check_true(a2.isActive);
do_check_false(hasFlag(a2.permissions, AddonManager.PERM_CAN_UNINSTALL));
run_test_2();
});
}
// Tests whether uninstalling from the registry works
function run_test_2() {
MockRegistry.setValue(AM_Ci.nsIWindowsRegKey.ROOT_KEY_LOCAL_MACHINE,
"SOFTWARE\\Mozilla\\XPCShell\\Extensions",
"addon1@tests.mozilla.org", null);
MockRegistry.setValue(AM_Ci.nsIWindowsRegKey.ROOT_KEY_CURRENT_USER,
"SOFTWARE\\Mozilla\\XPCShell\\Extensions",
"addon2@tests.mozilla.org", null);
restartManager(1);
AddonManager.getAddons(["addon1@tests.mozilla.org",
"addon2@tests.mozilla.org"], function([a1, a2]) {
do_check_eq(a1, null);
do_check_eq(a2, null);
run_test_3();
});
}
// Checks that the ID in the registry must match that in the install manifest
function run_test_3() {
MockRegistry.setValue(AM_Ci.nsIWindowsRegKey.ROOT_KEY_LOCAL_MACHINE,
"SOFTWARE\\Mozilla\\XPCShell\\Extensions",
"addon1@tests.mozilla.org", addon2Dir.path);
MockRegistry.setValue(AM_Ci.nsIWindowsRegKey.ROOT_KEY_CURRENT_USER,
"SOFTWARE\\Mozilla\\XPCShell\\Extensions",
"addon2@tests.mozilla.org", addon1Dir.path);
restartManager(0);
AddonManager.getAddons(["addon1@tests.mozilla.org",
"addon2@tests.mozilla.org"], function([a1, a2]) {
do_check_eq(a1, null);
do_check_eq(a2, null);
// Restarting with bad items in the registry should not force an EM restart
restartManager(0);
run_test_4();
});
}
// Tests whether an extension's ID can change without its directory changing
function run_test_4() {
MockRegistry.setValue(AM_Ci.nsIWindowsRegKey.ROOT_KEY_LOCAL_MACHINE,
"SOFTWARE\\Mozilla\\XPCShell\\Extensions",
"addon1@tests.mozilla.org", null);
MockRegistry.setValue(AM_Ci.nsIWindowsRegKey.ROOT_KEY_CURRENT_USER,
"SOFTWARE\\Mozilla\\XPCShell\\Extensions",
"addon2@tests.mozilla.org", null);
restartManager(0);
MockRegistry.setValue(AM_Ci.nsIWindowsRegKey.ROOT_KEY_LOCAL_MACHINE,
"SOFTWARE\\Mozilla\\XPCShell\\Extensions",
"addon1@tests.mozilla.org", addon1Dir.path);
restartManager(1);
MockRegistry.setValue(AM_Ci.nsIWindowsRegKey.ROOT_KEY_LOCAL_MACHINE,
"SOFTWARE\\Mozilla\\XPCShell\\Extensions",
"addon1@tests.mozilla.org", null);
MockRegistry.setValue(AM_Ci.nsIWindowsRegKey.ROOT_KEY_CURRENT_USER,
"SOFTWARE\\Mozilla\\XPCShell\\Extensions",
"addon2@tests.mozilla.org", addon1Dir.path);
writeInstallRDFToDir(addon2, addon1Dir);
restartManager(1);
AddonManager.getAddons(["addon1@tests.mozilla.org",
"addon2@tests.mozilla.org"], function([a1, a2]) {
do_check_eq(a1, null);
do_check_neq(a2, null);
do_test_finished();
});
}

View File

@ -120,6 +120,9 @@ function run_test_1() {
dest = profileDir.clone();
dest.append("addon2@tests.mozilla.org");
writeInstallRDFToDir(addon2, dest);
// Attempt to make this look like it was added some time in the past so
// the change in run_test_2 makes the last modified time change.
dest.lastModifiedTime -= 5000;
dest = profileDir.clone();
dest.append("addon3@tests.mozilla.org");
writeInstallRDFToDir(addon3, dest);