bug 851936 allow uninstall of builtin providers, r=markh

This commit is contained in:
Shane Caraveo 2013-04-03 20:39:37 -07:00
parent 9622db9ff7
commit b4f67200ab
10 changed files with 291 additions and 60 deletions

File diff suppressed because one or more lines are too long

View File

@ -15,6 +15,7 @@ _BROWSER_FILES = \
blocklist.xml \
blocklistEmpty.xml \
browser_blocklist.js \
browser_defaults.js \
browser_addons.js \
browser_social_activation.js \
browser_social_perwindowPB.js \

View File

@ -8,7 +8,7 @@ const ID_SUFFIX = "@services.mozilla.org";
const STRING_TYPE_NAME = "type.%ID%.name";
const XPINSTALL_URL = "chrome://mozapps/content/xpinstall/xpinstallConfirm.xul";
let manifest = { // normal provider
let manifest = { // builtin provider
name: "provider 1",
origin: "https://example.com",
sidebarURL: "https://example.com/browser/browser/base/content/test/social/social_sidebar.html",
@ -23,14 +23,45 @@ let manifest2 = { // used for testing install
iconURL: "https://test1.example.com/browser/browser/base/content/test/moz.png"
};
function getManifestPrefname(aManifest) {
// is same as the generated name in SocialServiceInternal.getManifestPrefname
let originUri = Services.io.newURI(aManifest.origin, null, null);
return "social.manifest." + originUri.hostPort.replace('.','-');
}
function setBuiltinManifestPref(name, manifest) {
// we set this as a default pref, it must not be a user pref
manifest.builtin = true;
let string = Cc["@mozilla.org/supports-string;1"].
createInstance(Ci.nsISupportsString);
string.data = JSON.stringify(manifest);
Services.prefs.getDefaultBranch(null).setComplexValue(name, Ci.nsISupportsString, string);
// verify this is set on the default branch
let stored = Services.prefs.getComplexValue(name, Ci.nsISupportsString).data;
is(stored, string.data, "manifest '"+name+"' stored in default prefs");
// don't dirty our manifest, we'll need it without this flag later
delete manifest.builtin;
// verify we DO NOT have a user-level pref
ok(!Services.prefs.prefHasUserValue(name), "manifest '"+name+"' is not in user-prefs");
}
function test() {
waitForExplicitFinish();
setManifestPref("social.manifest.good", manifest);
let prefname = getManifestPrefname(manifest);
setBuiltinManifestPref(prefname, manifest);
// ensure that manifest2 is NOT showing as builtin
is(SocialService.getOriginActivationType(manifest.origin), "builtin", "manifest is builtin");
is(SocialService.getOriginActivationType(manifest2.origin), "foreign", "manifest2 is not builtin");
Services.prefs.setBoolPref("social.remote-install.enabled", true);
runSocialTests(tests, undefined, undefined, function () {
Services.prefs.clearUserPref("social.remote-install.enabled");
Services.prefs.clearUserPref("social.manifest.good");
// clear our builtin pref
ok(!Services.prefs.prefHasUserValue(prefname), "manifest is not in user-prefs");
Services.prefs.getDefaultBranch(null).deleteBranch(prefname);
is(Services.prefs.getDefaultBranch(null).getPrefType(prefname),
Services.prefs.PREF_INVALID, "default pref removed");
// just in case the tests failed, clear these here as well
Services.prefs.clearUserPref("social.whitelist");
Services.prefs.clearUserPref("social.directories");
@ -38,26 +69,31 @@ function test() {
});
}
function installListener(next) {
function installListener(next, aManifest) {
let expectEvent = "onInstalling";
let prefname = getManifestPrefname(aManifest);
return {
onInstalling: function(addon) {
is(expectEvent, "onInstalling", "install started");
is(addon.manifest.origin, manifest2.origin, "provider about to be installed");
is(addon.manifest.origin, aManifest.origin, "provider about to be installed");
ok(!Services.prefs.prefHasUserValue(prefname), "manifest is not in user-prefs");
expectEvent = "onInstalled";
},
onInstalled: function(addon) {
is(addon.manifest.origin, manifest2.origin, "provider installed");
is(addon.manifest.origin, aManifest.origin, "provider installed");
ok(Services.prefs.prefHasUserValue(prefname), "manifest is in user-prefs");
expectEvent = "onUninstalling";
},
onUninstalling: function(addon) {
is(expectEvent, "onUninstalling", "uninstall started");
is(addon.manifest.origin, manifest2.origin, "provider about to be uninstalled");
is(addon.manifest.origin, aManifest.origin, "provider about to be uninstalled");
ok(Services.prefs.prefHasUserValue(prefname), "manifest is in user-prefs");
expectEvent = "onUninstalled";
},
onUninstalled: function(addon) {
is(expectEvent, "onUninstalled", "provider has been uninstalled");
is(addon.manifest.origin, manifest2.origin, "provider uninstalled");
is(addon.manifest.origin, aManifest.origin, "provider uninstalled");
ok(!Services.prefs.prefHasUserValue(prefname), "manifest is not in user-prefs");
AddonManager.removeAddonListener(this);
next();
}
@ -65,29 +101,9 @@ function installListener(next) {
}
var tests = {
testInstalledProviders: function(next) {
// tests that our builtin manfests are actually available to the addon
// manager. We may have interference from the real builtin providers, so
// we will expect our test provider above to be in the list
AddonManager.getAddonsByTypes([ADDON_TYPE_SERVICE], function(addons) {
for (let addon of addons) {
if (addon.manifest.origin == manifest.origin) {
ok(true, "test addon is installed");
next();
return;
}
}
// failure state
ok(false, "test addon is not installed");
next();
});
},
testAddonEnableToggle: function(next) {
// take the first addon in the list, and toggle its enabled state via the
// addon interface to see that we get events. restore the enabled state at
// the end.
let expectEvent;
let prefname = getManifestPrefname(manifest);
let listener = {
onEnabled: function(addon) {
is(expectEvent, "onEnabled", "provider onEnabled");
@ -109,6 +125,8 @@ var tests = {
// restore previous state
AddonManager.removeAddonListener(listener);
addon.userDisabled = !addon.userDisabled;
// clear the provider user-level pref
Services.prefs.clearUserPref(prefname);
next();
});
},
@ -119,6 +137,10 @@ var tests = {
};
AddonManager.addAddonListener(listener);
// we're only testing enable disable, so we quickly set the user-level pref
// for this provider and test enable/disable toggling
setManifestPref(prefname, manifest);
ok(Services.prefs.prefHasUserValue(prefname), "manifest is in user-prefs");
AddonManager.getAddonsByTypes([ADDON_TYPE_SERVICE], function(addons) {
for (let addon of addons) {
expectEvent = addon.userDisabled ? "onEnabling" : "onDisabling";
@ -135,6 +157,7 @@ var tests = {
// that the addon manager is updated
let expectEvent;
let prefname = getManifestPrefname(manifest);
let listener = {
onEnabled: function(addon) {
@ -161,16 +184,18 @@ var tests = {
AddonManager.addAddonListener(listener);
expectEvent = "onEnabling";
setManifestPref(prefname, manifest);
SocialService.addBuiltinProvider(manifest.origin, function(provider) {
expectEvent = "onDisabling";
SocialService.removeProvider(provider.origin, function() {
AddonManager.removeAddonListener(listener);
Services.prefs.clearUserPref(prefname);
next();
});
});
},
testForeignInstall: function(next) {
AddonManager.addAddonListener(installListener(next));
AddonManager.addAddonListener(installListener(next, manifest2));
// we expect the addon install dialog to appear, we need to accept the
// install from the dialog.
@ -197,8 +222,27 @@ var tests = {
});
});
},
testBuiltinInstall: function(next) {
AddonManager.addAddonListener(installListener(next, manifest));
let prefname = getManifestPrefname(manifest);
let activationURL = manifest.origin + "/browser/browser/base/content/test/social/social_activate.html"
addTab(activationURL, function(tab) {
let doc = tab.linkedBrowser.contentDocument;
let installFrom = doc.nodePrincipal.origin;
is(SocialService.getOriginActivationType(installFrom), "builtin", "testing builtin install");
ok(!Services.prefs.prefHasUserValue(prefname), "manifest is not in user-prefs");
Social.installProvider(doc, manifest, function(addonManifest) {
ok(Services.prefs.prefHasUserValue(prefname), "manifest is in user-prefs");
SocialService.addBuiltinProvider(addonManifest.origin, function(provider) {
Social.uninstallProvider(addonManifest.origin);
gBrowser.removeTab(tab);
});
});
});
},
testWhitelistInstall: function(next) {
AddonManager.addAddonListener(installListener(next));
AddonManager.addAddonListener(installListener(next, manifest2));
let activationURL = manifest2.origin + "/browser/browser/base/content/test/social/social_activate.html"
addTab(activationURL, function(tab) {
@ -216,7 +260,7 @@ var tests = {
});
},
testDirectoryInstall: function(next) {
AddonManager.addAddonListener(installListener(next));
AddonManager.addAddonListener(installListener(next, manifest2));
let activationURL = manifest2.origin + "/browser/browser/base/content/test/social/social_activate.html"
addTab(activationURL, function(tab) {

View File

@ -0,0 +1,14 @@
let SocialService = Cu.import("resource://gre/modules/SocialService.jsm", {}).SocialService;
// this test ensures that any builtin providers have the builtin flag that we
// need to help with "install" of a builtin.
function test() {
let manifestPrefs = Services.prefs.getDefaultBranch("social.manifest.");
let prefs = manifestPrefs.getChildList("", []);
ok(prefs.length > 0, "we have builtin providers");
for (let pref of prefs) {
let manifest = JSON.parse(manifestPrefs.getComplexValue(pref, Ci.nsISupportsString).data);
ok(manifest.builtin, "manifest is builtin " + manifest.origin);
}
}

View File

@ -33,10 +33,13 @@ let SocialServiceInternal = {
return [p for ([, p] of Iterator(this.providers))];
},
get manifests() {
// Retrieve the builtin manifests from prefs
// Retrieve the manifests of installed providers from prefs
let MANIFEST_PREFS = Services.prefs.getBranch("social.manifest.");
let prefs = MANIFEST_PREFS.getChildList("", []);
for (let pref of prefs) {
// we only consider manifests in user level prefs to be *installed*
if (!MANIFEST_PREFS.prefHasUserValue(pref))
continue;
try {
var manifest = JSON.parse(MANIFEST_PREFS.getComplexValue(pref, Ci.nsISupportsString).data);
if (manifest && typeof(manifest) == "object" && manifest.origin)
@ -62,7 +65,7 @@ let SocialServiceInternal = {
let prefs = MANIFEST_PREFS.getChildList("", []);
for (let pref of prefs) {
try {
var manifest = JSON.parse(MANIFEST_PREFS.getCharPref(pref));
var manifest = JSON.parse(MANIFEST_PREFS.getComplexValue(pref, Ci.nsISupportsString).data);
if (manifest.origin == origin) {
return pref;
}
@ -121,21 +124,65 @@ let ActiveProviders = {
};
function migrateSettings() {
let activeProviders;
try {
// we don't care what the value is, if it is set, we've already migrated
Services.prefs.getCharPref("social.activeProviders");
return;
activeProviders = Services.prefs.getCharPref("social.activeProviders");
} catch(e) {
try {
let active = Services.prefs.getBoolPref("social.active");
if (active) {
for (let manifest of SocialServiceInternal.manifests) {
ActiveProviders.add(manifest.origin);
return;
}
// do nothing
}
if (activeProviders) {
// migration from fx21 to fx22 or later
// ensure any *builtin* provider in activeproviders is in user level prefs
for (let origin in ActiveProviders._providers) {
let prefname = getPrefnameFromOrigin(origin);
if (!Services.prefs.prefHasUserValue(prefname)) {
// if we've got an active *builtin* provider, ensure that the pref
// is set at a user-level as that will signify *installed* status.
let manifest = JSON.parse(MANIFEST_PREFS.getComplexValue(prefname, Ci.nsISupportsString).data);
// ensure we override a builtin manifest by having a different value in it
if (manifest.builtin)
delete manifest.builtin;
let string = Cc["@mozilla.org/supports-string;1"].
createInstance(Ci.nsISupportsString);
string.data = JSON.stringify(manifest);
Services.prefs.setComplexValue(prefname, Ci.nsISupportsString, string);
}
} catch(e) {
// not activated, nothing to see here.
}
return;
}
// primary migration from pre-fx21
let active;
try {
active = Services.prefs.getBoolPref("social.active");
} catch(e) {}
if (!active)
return;
// primary difference from SocialServiceInternal.manifests is that we
// only read the default branch here.
let manifestPrefs = Services.prefs.getDefaultBranch("social.manifest.");
let prefs = manifestPrefs.getChildList("", []);
for (let pref of prefs) {
try {
let manifest = JSON.parse(manifestPrefs.getComplexValue(pref, Ci.nsISupportsString).data);
if (manifest && typeof(manifest) == "object" && manifest.origin) {
// ensure we override a builtin manifest by having a different value in it
if (manifest.builtin)
delete manifest.builtin;
let string = Cc["@mozilla.org/supports-string;1"].createInstance(Ci.nsISupportsString);
string.data = JSON.stringify(manifest);
Services.prefs.setComplexValue(pref, Ci.nsISupportsString, string);
ActiveProviders.add(manifest.origin);
ActiveProviders.flush();
// social.active was used at a time that there was only one
// builtin, we'll assume that is still the case
return;
}
} catch (err) {
Cu.reportError("SocialService: failed to load manifest: " + pref + ", exception: " + err);
}
}
}
@ -291,10 +338,9 @@ this.SocialService = {
},
getOriginActivationType: function(origin) {
for (let manifest in SocialServiceInternal.manifests) {
if (manifest.origin == origin)
return 'builtin';
}
let prefname = SocialServiceInternal.getManifestPrefname(origin);
if (Services.prefs.getDefaultBranch("social.manifest.").getPrefType(prefname) == Services.prefs.PREF_STRING)
return 'builtin';
let whitelist = Services.prefs.getCharPref("social.whitelist").split(',');
if (whitelist.indexOf(origin) >= 0)
@ -445,8 +491,15 @@ this.SocialService = {
// no way to know what provider we're trying to enable. This is
// primarily an issue for "version zero" providers that did not
// send the manifest with the dom event for activation.
if (!manifest)
manifest = SocialServiceInternal.getManifestByOrigin(installOrigin);
if (!manifest) {
let prefname = getPrefnameFromOrigin(installOrigin);
manifest = Services.prefs.getDefaultBranch(prefname)
.getComplexValue(prefname, Ci.nsISupportsString).data;
manifest = JSON.parse(manifest);
// ensure we override a builtin manifest by having a different value in it
if (manifest.builtin)
delete manifest.builtin;
}
case "directory":
// a manifest is requried, and will have been vetted by reviewers
case "whitelist":

View File

@ -57,6 +57,17 @@ function createAppInfo(id, name, version, platformVersion) {
XULAPPINFO_CONTRACTID, XULAppInfoFactory);
}
function initApp() {
createAppInfo("xpcshell@tests.mozilla.org", "XPCShell", "1", "1.9");
// prepare a blocklist file for the blocklist service
var blocklistFile = gProfD.clone();
blocklistFile.append("blocklist.xml");
if (blocklistFile.exists())
blocklistFile.remove(false);
var source = do_get_file("blocklist.xml");
source.copyTo(gProfD, "blocklist.xml");
}
function AsyncRunner() {
do_test_pending();
do_register_cleanup((function () this.destroy()).bind(this));

View File

@ -5,14 +5,7 @@
Cu.import("resource://gre/modules/Services.jsm");
function run_test() {
createAppInfo("xpcshell@tests.mozilla.org", "XPCShell", "1", "1.9");
// prepare a blocklist file for the blocklist service
var blocklistFile = gProfD.clone();
blocklistFile.append("blocklist.xml");
if (blocklistFile.exists())
blocklistFile.remove(false);
var source = do_get_file("blocklist.xml");
source.copyTo(gProfD, "blocklist.xml");
initApp();
// NOTE: none of the manifests here can have a workerURL set, or we attempt
// to create a FrameWorker and that fails under xpcshell...

View File

@ -0,0 +1,51 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
* You can obtain one at http://mozilla.org/MPL/2.0/. */
Cu.import("resource://gre/modules/Services.jsm");
const DEFAULT_PREFS = Services.prefs.getDefaultBranch("social.manifest.");
function run_test() {
// Test must run at startup for migration to occur, so we can only test
// one migration per test file
initApp();
// NOTE: none of the manifests here can have a workerURL set, or we attempt
// to create a FrameWorker and that fails under xpcshell...
let manifest = { // normal provider
name: "provider 1",
origin: "https://example1.com",
};
DEFAULT_PREFS.setCharPref(manifest.origin, JSON.stringify(manifest));
Services.prefs.setBoolPref("social.active", true);
// Enable the service for this test
Services.prefs.setBoolPref("social.enabled", true);
Cu.import("resource://gre/modules/SocialService.jsm");
let runner = new AsyncRunner();
let next = runner.next.bind(runner);
runner.appendIterator(testMigration(manifest, next));
runner.next();
}
function testMigration(manifest, next) {
// look at social.activeProviders, we should have migrated into that, and
// we should be set as a user level pref after migration
do_check_false(MANIFEST_PREFS.prefHasUserValue(manifest.origin));
// we need to access the providers for everything to initialize
SocialService.getProviderList(function(providers) {
do_check_true(SocialService.enabled);
do_check_true(Services.prefs.prefHasUserValue("social.activeProviders"));
let activeProviders;
let pref = Services.prefs.getComplexValue("social.activeProviders",
Ci.nsISupportsString);
activeProviders = JSON.parse(pref);
do_check_true(activeProviders.has(manifest.origin));
do_check_true(MANIFEST_PREFS.prefHasUserValue(manifest.origin));
});
yield true;
}

View File

@ -0,0 +1,60 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
* You can obtain one at http://mozilla.org/MPL/2.0/. */
Cu.import("resource://gre/modules/Services.jsm");
const DEFAULT_PREFS = Services.prefs.getDefaultBranch("social.manifest.");
function run_test() {
// Test must run at startup for migration to occur, so we can only test
// one migration per test file
initApp();
// NOTE: none of the manifests here can have a workerURL set, or we attempt
// to create a FrameWorker and that fails under xpcshell...
let manifest = { // normal provider
name: "provider 1",
origin: "https://example1.com",
};
DEFAULT_PREFS.setCharPref(manifest.origin, JSON.stringify(manifest));
// Set both providers active and flag the first one as "current"
let activeVal = Cc["@mozilla.org/supports-string;1"].
createInstance(Ci.nsISupportsString);
let active = {};
active[manifest.origin] = 1;
activeVal.data = JSON.stringify(active);
Services.prefs.setComplexValue("social.activeProviders",
Ci.nsISupportsString, activeVal);
Services.prefs.setCharPref("social.provider.current", manifest.origin);
// Enable the service for this test
Services.prefs.setBoolPref("social.enabled", true);
Cu.import("resource://gre/modules/SocialService.jsm");
let runner = new AsyncRunner();
let next = runner.next.bind(runner);
runner.appendIterator(testMigration(manifest, next));
runner.next();
}
function testMigration(manifest, next) {
// look at social.activeProviders, we should have migrated into that, and
// we should be set as a user level pref after migration
do_check_false(MANIFEST_PREFS.prefHasUserValue(manifest.origin));
// we need to access the providers for everything to initialize
SocialService.getProviderList(function(providers) {
do_check_true(SocialService.enabled);
do_check_true(Services.prefs.prefHasUserValue("social.activeProviders"));
let activeProviders;
let pref = Services.prefs.getComplexValue("social.activeProviders",
Ci.nsISupportsString);
activeProviders = JSON.parse(pref);
do_check_true(activeProviders.has(manifest.origin));
do_check_true(MANIFEST_PREFS.prefHasUserValue(manifest.origin));
});
yield true;
}

View File

@ -3,3 +3,7 @@ head = head.js
tail =
[test_SocialService.js]
[test_SocialServiceMigration21.js]
[test_SocialServiceMigration22.js]