Bug 975000 - Disable updating and compatibility checking for Experiments; r=Unfocused

Experiment add-ons are installed and updated via the Experiments Manager
service. With this change, the Add-ons Manager lets experiment add-ons
play by their own rules without interference.

--HG--
extra : rebase_source : 12f990198fed7171b9572c24aa2f8319620414a2
This commit is contained in:
Gregory Szorc 2014-03-28 20:17:04 +01:00
Родитель cafb852187
Коммит 44de0dbb4d
8 изменённых файлов: 154 добавлений и 14 удалений

Просмотреть файл

@ -2317,7 +2317,25 @@ this.AddonManagerPrivate = {
return {
done: () => this.recordSimpleMeasure(aName, Date.now() - startTime)
};
}
},
/**
* Helper to call update listeners when no update is available.
*
* This can be used as an implementation for Addon.findUpdates() when
* no update mechanism is available.
*/
callNoUpdateListeners: function (addon, listener, reason, appVersion, platformVersion) {
if ("onNoCompatibilityUpdateAvailable" in listener) {
safeCall(listener.onNoCompatibilityUpdateAvailable.bind(listener), addon);
}
if ("onNoUpdateAvailable" in listener) {
safeCall(listener.onNoUpdateAvailable.bind(listener), addon);
}
if ("onUpdateFinished" in listener) {
safeCall(listener.onUpdateFinished.bind(listener), addon);
}
},
};
/**

Просмотреть файл

@ -508,12 +508,7 @@ function AddonWrapper(aTheme) {
};
this.findUpdates = function AddonWrapper_findUpdates(listener, reason, appVersion, platformVersion) {
if ("onNoCompatibilityUpdateAvailable" in listener)
listener.onNoCompatibilityUpdateAvailable(this);
if ("onNoUpdateAvailable" in listener)
listener.onNoUpdateAvailable(this);
if ("onUpdateFinished" in listener)
listener.onUpdateFinished(this);
AddonManagerPrivate.callNoUpdateListeners(this, listener, reason, appVersion, platformVersion);
};
}

Просмотреть файл

@ -915,6 +915,17 @@ function loadManifestFromRDF(aUri, aStream) {
addon.applyBackgroundUpdates = AddonManager.AUTOUPDATE_DEFAULT;
// Experiments are managed and updated through an external "experiments
// manager." So disable some built-in mechanisms.
if (addon.type == "experiment") {
addon.applyBackgroundUpdates = AddonManager.AUTOUPDATE_DISABLE;
addon.updateURL = null;
addon.updateKey = null;
addon.targetApplications = [];
addon.targetPlatforms = [];
}
// Load the storage service before NSS (nsIRandomGenerator),
// to avoid a SQLite initialization error (bug 717904).
let storage = Services.storage;
@ -6014,6 +6025,17 @@ AddonInternal.prototype = {
},
isCompatibleWith: function AddonInternal_isCompatibleWith(aAppVersion, aPlatformVersion) {
// Experiments are installed through an external mechanism that
// limits target audience to compatible clients. We trust it knows what
// it's doing and skip compatibility checks.
//
// This decision does forfeit defense in depth. If the experiments system
// is ever wrong about targeting an add-on to a specific application
// or platform, the client will likely see errors.
if (this.type == "experiment") {
return true;
}
let app = this.matchingTargetApplication;
if (!app)
return false;
@ -6398,6 +6420,11 @@ function AddonWrapper(aAddon) {
return aAddon.applyBackgroundUpdates;
});
this.__defineSetter__("applyBackgroundUpdates", function AddonWrapper_applyBackgroundUpdatesSetter(val) {
if (this.type == "experiment") {
logger.warn("Setting applyBackgroundUpdates on an experiment is not supported.");
return;
}
if (val != AddonManager.AUTOUPDATE_DEFAULT &&
val != AddonManager.AUTOUPDATE_DISABLE &&
val != AddonManager.AUTOUPDATE_ENABLE) {
@ -6498,22 +6525,33 @@ function AddonWrapper(aAddon) {
if (!(aAddon.inDatabase))
return permissions;
// Experiments can only be uninstalled. An uninstall reflects the user
// intent of "disable this experiment." This is partially managed by the
// experiments manager.
if (aAddon.type == "experiment") {
return AddonManager.PERM_CAN_UNINSTALL;
}
if (!aAddon.appDisabled) {
if (this.userDisabled)
if (this.userDisabled) {
permissions |= AddonManager.PERM_CAN_ENABLE;
else if (aAddon.type != "theme")
}
else if (aAddon.type != "theme") {
permissions |= AddonManager.PERM_CAN_DISABLE;
}
}
// Add-ons that are in locked install locations, or are pending uninstall
// cannot be upgraded or uninstalled
if (!aAddon._installLocation.locked && !aAddon.pendingUninstall) {
// Add-ons that are installed by a file link cannot be upgraded
if (!aAddon._installLocation.isLinkedAddon(aAddon.id))
if (!aAddon._installLocation.isLinkedAddon(aAddon.id)) {
permissions |= AddonManager.PERM_CAN_UPGRADE;
}
permissions |= AddonManager.PERM_CAN_UNINSTALL;
}
return permissions;
});
@ -6595,6 +6633,14 @@ function AddonWrapper(aAddon) {
};
this.findUpdates = function AddonWrapper_findUpdates(aListener, aReason, aAppVersion, aPlatformVersion) {
// Short-circuit updates for experiments because updates are handled
// through the Experiments Manager.
if (this.type == "experiment") {
AddonManagerPrivate.callNoUpdateListeners(this, aListener, aReason,
aAppVersion, aPlatformVersion);
return;
}
new UpdateChecker(aAddon, aListener, aReason, aAppVersion, aPlatformVersion);
};

Просмотреть файл

@ -0,0 +1,17 @@
<?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>experiment1@tests.mozilla.org</em:id>
<em:version>1.0</em:version>
<em:type>128</em:type>
<em:bootstrap>true</em:bootstrap>
<!-- Front End MetaData -->
<em:name>Test Experiment 1</em:name>
<em:description>Test Description</em:description>
</Description>
</RDF>

Просмотреть файл

@ -50,9 +50,7 @@ add_test(function testActiveExperiment() {
install_addon("addons/browser_experiment1.xpi", (addon) => {
gInstalledAddons.push(addon);
// This may change if we remove compatibility checking from experiments.
// Putting this check here so a test fails if preconditions change.
Assert.equal(addon.isActive, false, "Add-on is not active.");
Assert.ok(addon.isActive, "Add-on is active.");
Assert.ok(gCategoryUtilities.isTypeVisible("experiment"), "Experiment tab visible.");
@ -133,3 +131,19 @@ add_test(function testOpenPreferences() {
EventUtils.synthesizeMouseAtCenter(btn, {}, gManagerWindow);
});
});
add_test(function testButtonPresence() {
gCategoryUtilities.openType("experiment", (win) => {
let item = get_addon_element(gManagerWindow, "test-experiment1@experiments.mozilla.org");
Assert.ok(item, "Got add-on element.");
let el = item.ownerDocument.getAnonymousElementByAttribute(item, "anonid", "remove-btn");
// Corresponds to the uninstall permission.
is_element_visible(el, "Remove button is visible.");
// Corresponds to lack of disable permission.
el = item.ownerDocument.getAnonymousElementByAttribute(item, "anonid", "disable-btn");
is_element_hidden(el, "Disable button not visible.");
run_next_test();
});
});

Просмотреть файл

@ -0,0 +1,48 @@
/* Any copyright is dedicated to the Public Domain.
* http://creativecommons.org/publicdomain/zero/1.0/ */
function run_test() {
createAppInfo("xpcshell@tests.mozilla.org", "XPCShell", "1", "1.9.2");
startupManager();
run_next_test();
}
add_test(function test_experiment() {
AddonManager.getInstallForFile(do_get_addon("test_experiment1"), (install) => {
completeAllInstalls([install], () => {
AddonManager.getAddonByID("experiment1@tests.mozilla.org", (addon) => {
Assert.ok(addon, "Addon is found.");
Assert.ok(addon.isActive, "Add-on is active.");
Assert.equal(addon.updateURL, null, "No updateURL for experiments.");
Assert.equal(addon.applyBackgroundUpdates, AddonManager.AUTOUPDATE_DISABLE,
"Background updates are disabled.");
Assert.equal(addon.permissions, AddonManager.PERM_CAN_UNINSTALL,
"Permissions are minimal.");
// Setting applyBackgroundUpdates should not work.
addon.applyBackgroundUpdates = AddonManager.AUTOUPDATE_ENABLE;
Assert.equal(addon.applyBackgroundUpdates, AddonManager.AUTOUPDATE_DISABLE,
"Setting applyBackgroundUpdates shouldn't do anything.");
let noCompatibleCalled = false;
let noUpdateCalled = false;
let finishedCalled = false;
let listener = {
onNoCompatibilityUpdateAvailable: () => { noCompatibleCalled = true; },
onNoUpdateAvailable: () => { noUpdateCalled = true; },
onUpdateFinished: () => { finishedCalled = true; },
};
addon.findUpdates(listener, "testing", null, null);
Assert.ok(noCompatibleCalled, "Listener called.");
Assert.ok(noUpdateCalled, "Listener called.");
Assert.ok(finishedCalled, "Listener called.");
run_next_test();
});
});
});
});

Просмотреть файл

@ -17,7 +17,8 @@ const IGNORE_PRIVATE = ["AddonAuthor", "AddonCompatibilityOverride",
"addStartupChange", "removeStartupChange",
"recordTimestamp", "recordSimpleMeasure",
"recordException", "getSimpleMeasures", "simpleTimer",
"setTelemetryDetails", "getTelemetryDetails"];
"setTelemetryDetails", "getTelemetryDetails",
"callNoUpdateListeners"];
function test_functions() {
for (let prop in AddonManager) {

Просмотреть файл

@ -163,6 +163,7 @@ fail-if = os == "android"
# Bug 676992: test consistently hangs on Android
skip-if = os == "android"
[test_error.js]
[test_experiment.js]
[test_filepointer.js]
# Bug 676992: test consistently hangs on Android
skip-if = os == "android"