Bug 617289: PluginProvider doesn't keep IDs of plugins consistent between restarts. r=robstrong

This commit is contained in:
Dave Townsend 2011-04-19 14:53:07 -07:00
parent acc6ecc70e
commit 0e53288b95
3 changed files with 235 additions and 20 deletions

View File

@ -54,10 +54,41 @@ Components.utils.import("resource://gre/modules/Services.jsm");
});
}, this);
function getIDHashForString(aStr) {
// return the two-digit hexadecimal code for a byte
function toHexString(charCode)
("0" + charCode.toString(16)).slice(-2);
let hasher = Cc["@mozilla.org/security/hash;1"].
createInstance(Ci.nsICryptoHash);
hasher.init(Ci.nsICryptoHash.MD5);
let stringStream = Cc["@mozilla.org/io/string-input-stream;1"].
createInstance(Ci.nsIStringInputStream);
stringStream.data = aStr ? aStr : "null";
hasher.updateFromStream(stringStream, -1);
// convert the binary hash data to a hex string.
let binary = hasher.finish(false);
let hash = [toHexString(binary.charCodeAt(i)) for (i in binary)].join("").toLowerCase();
return "{" + hash.substr(0, 8) + "-" +
hash.substr(8, 4) + "-" +
hash.substr(12, 4) + "-" +
hash.substr(16, 4) + "-" +
hash.substr(20) + "}";
}
var PluginProvider = {
// A dictionary mapping IDs to names and descriptions
plugins: null,
/**
* Called when the application is shutting down. Only necessary for tests
* to be able to simulate a shutdown.
*/
shutdown: function PL_shutdown() {
this.plugins = null;
},
/**
* Called to get an Addon with a particular ID.
*
@ -73,17 +104,9 @@ var PluginProvider = {
if (aId in this.plugins) {
let name = this.plugins[aId].name;
let description = this.plugins[aId].description;
let tags = this.plugins[aId].tags;
let tags = Cc["@mozilla.org/plugin/host;1"].
getService(Ci.nsIPluginHost).
getPluginTags({});
let selected = [];
tags.forEach(function(aTag) {
if (aTag.name == name && aTag.description == description)
selected.push(aTag);
}, this);
aCallback(new PluginWrapper(aId, name, description, selected));
aCallback(new PluginWrapper(aId, name, description, tags));
}
else {
aCallback(null);
@ -148,19 +171,24 @@ var PluginProvider = {
getPluginTags({});
this.plugins = {};
let seen = {};
let plugins = {};
tags.forEach(function(aTag) {
if (!(aTag.name in seen))
seen[aTag.name] = {};
if (!(aTag.description in seen[aTag.name])) {
let id = Cc["@mozilla.org/uuid-generator;1"].
getService(Ci.nsIUUIDGenerator).
generateUUID();
this.plugins[id] = {
if (!(aTag.name in plugins))
plugins[aTag.name] = {};
if (!(aTag.description in plugins[aTag.name])) {
let plugin = {
name: aTag.name,
description: aTag.description
description: aTag.description,
tags: [aTag]
};
seen[aTag.name][aTag.description] = true;
let id = getIDHashForString(aTag.name + aTag.description);
plugins[aTag.name][aTag.description] = plugin;
this.plugins[id] = plugin;
}
else {
plugins[aTag.name][aTag.description].tags.push(aTag);
}
}, this);
}

View File

@ -0,0 +1,175 @@
/* Any copyright is dedicated to the Public Domain.
* http://creativecommons.org/publicdomain/zero/1.0/
*/
// This verifies that duplicate plugins are coalesced and maintain their ID
// across restarts.
var PLUGINS = [{
name: "Duplicate Plugin 1",
description: "A duplicate plugin",
version: "1",
blocklisted: false,
disabled: false,
filename: "/home/mozilla/.plugins/dupplugin1.so"
}, {
name: "Duplicate Plugin 1",
description: "A duplicate plugin",
version: "1",
blocklisted: false,
disabled: false,
filename: "",
filename: "/usr/lib/plugins/dupplugin1.so"
}, {
name: "Duplicate Plugin 2",
description: "Another duplicate plugin",
version: "1",
blocklisted: false,
disabled: false,
filename: "/home/mozilla/.plugins/dupplugin2.so"
}, {
name: "Duplicate Plugin 2",
description: "Another duplicate plugin",
version: "1",
blocklisted: false,
disabled: false,
filename: "",
filename: "/usr/lib/plugins/dupplugin2.so"
}, {
name: "Non-duplicate Plugin", // 3
description: "Not a duplicate plugin",
version: "1",
blocklisted: false,
disabled: false,
filename: "/home/mozilla/.plugins/dupplugin3.so"
}, {
name: "Non-duplicate Plugin", // 4
description: "Not a duplicate because the descriptions are different",
version: "1",
blocklisted: false,
disabled: false,
filename: "",
filename: "/usr/lib/plugins/dupplugin4.so"
}, {
name: "Another Non-duplicate Plugin", // 5
description: "Not a duplicate plugin",
version: "1",
blocklisted: false,
disabled: false,
filename: "/home/mozilla/.plugins/dupplugin5.so"
}];
// A fake plugin host to return the plugins defined above
var PluginHost = {
getPluginTags: function(countRef) {
countRef.value = PLUGINS.length;
return PLUGINS;
},
QueryInterface: function(iid) {
if (iid.equals(Components.interfaces.nsIPluginHost)
|| iid.equals(Components.interfaces.nsISupports))
return this;
throw Components.results.NS_ERROR_NO_INTERFACE;
}
}
var PluginHostFactory = {
createInstance: function (outer, iid) {
if (outer != null)
throw Components.results.NS_ERROR_NO_AGGREGATION;
return PluginHost.QueryInterface(iid);
}
};
var registrar = Components.manager.QueryInterface(Components.interfaces.nsIComponentRegistrar);
registrar.registerFactory(Components.ID("{721c3e73-969e-474b-a6dc-059fd288c428}"),
"Fake Plugin Host",
"@mozilla.org/plugin/host;1", PluginHostFactory);
var gPluginIDs = [null, null, null, null, null];
function run_test() {
do_test_pending();
createAppInfo("xpcshell@tests.mozilla.org", "XPCShell", "1", "1.9.2");
startupManager();
run_test_1();
}
function found_plugin(aNum, aId) {
if (gPluginIDs[aNum])
do_throw("Found duplicate of plugin " + aNum);
gPluginIDs[aNum] = aId;
}
// Test that the plugins were coalesced and all appear in the returned list
function run_test_1() {
AddonManager.getAddonsByTypes(["plugin"], function(aAddons) {
do_check_eq(aAddons.length, 5);
aAddons.forEach(function(aAddon) {
if (aAddon.name == "Duplicate Plugin 1") {
found_plugin(0, aAddon.id);
do_check_eq(aAddon.description, "A duplicate plugin");
}
else if (aAddon.name == "Duplicate Plugin 2") {
found_plugin(1, aAddon.id);
do_check_eq(aAddon.description, "Another duplicate plugin");
}
else if (aAddon.name == "Another Non-duplicate Plugin") {
found_plugin(5, aAddon.id);
do_check_eq(aAddon.description, "Not a duplicate plugin");
}
else if (aAddon.name == "Non-duplicate Plugin") {
if (aAddon.description == "Not a duplicate plugin")
found_plugin(3, aAddon.id);
else if (aAddon.description == "Not a duplicate because the descriptions are different")
found_plugin(4, aAddon.id);
else
do_throw("Found unexpected plugin with description " + aAddon.description);
}
else {
do_throw("Found unexpected plugin " + aAddon.name);
}
});
run_test_2();
});
}
// Test that disabling a coalesced plugin disables all its tags
function run_test_2() {
AddonManager.getAddonByID(gPluginIDs[0], function(p) {
do_check_false(p.userDisabled);
p.userDisabled = true;
do_check_true(PLUGINS[0].disabled);
do_check_true(PLUGINS[1].disabled);
run_test_3();
});
}
// Test that IDs persist across restart
function run_test_3() {
restartManager();
AddonManager.getAddonByID(gPluginIDs[0], function(p) {
do_check_neq(p, null);
do_check_eq(p.name, "Duplicate Plugin 1");
do_check_eq(p.description, "A duplicate plugin");
// Reorder the plugins and restart again
[PLUGINS[0], PLUGINS[1]] = [PLUGINS[1], PLUGINS[0]];
restartManager();
AddonManager.getAddonByID(gPluginIDs[0], function(p) {
do_check_neq(p, null);
do_check_eq(p.name, "Duplicate Plugin 1");
do_check_eq(p.description, "A duplicate plugin");
do_test_finished();
});
});
}

View File

@ -166,6 +166,18 @@ function run_test_3(p) {
do_check_true(p.isActive);
do_check_eq(p.name, "Test Plug-in");
run_test_4();
});
}
// Verify that after a restart the test plugin has the same ID
function run_test_4() {
restartManager();
AddonManager.getAddonByID(gID, function(p) {
do_check_neq(p, null);
do_check_eq(p.name, "Test Plug-in");
do_test_finished();
});
}