mirror of
https://gitlab.winehq.org/wine/wine-gecko.git
synced 2024-09-13 09:24:08 -07:00
Bug 990111 - Add-on provider for historical experiments. r=irving
This commit is contained in:
parent
8f334896b2
commit
d81e1e6024
@ -24,6 +24,8 @@ XPCOMUtils.defineLazyModuleGetter(this, "UpdateChannel",
|
||||
"resource://gre/modules/UpdateChannel.jsm");
|
||||
XPCOMUtils.defineLazyModuleGetter(this, "AddonManager",
|
||||
"resource://gre/modules/AddonManager.jsm");
|
||||
XPCOMUtils.defineLazyModuleGetter(this, "AddonManagerPrivate",
|
||||
"resource://gre/modules/AddonManager.jsm");
|
||||
XPCOMUtils.defineLazyModuleGetter(this, "TelemetryPing",
|
||||
"resource://gre/modules/TelemetryPing.jsm");
|
||||
XPCOMUtils.defineLazyModuleGetter(this, "TelemetryLog",
|
||||
@ -70,6 +72,9 @@ const PREF_HEALTHREPORT_ENABLED = "datareporting.healthreport.service.enabled";
|
||||
const PREF_BRANCH_TELEMETRY = "toolkit.telemetry.";
|
||||
const PREF_TELEMETRY_ENABLED = "enabled";
|
||||
|
||||
const URI_EXTENSION_STRINGS = "chrome://mozapps/locale/extensions/extensions.properties";
|
||||
const STRING_TYPE_NAME = "type.%ID%.name";
|
||||
|
||||
const TELEMETRY_LOG = {
|
||||
// log(key, [kind, experimentId, details])
|
||||
ACTIVATION_KEY: "EXPERIMENT_ACTIVATION",
|
||||
@ -103,6 +108,7 @@ const TELEMETRY_LOG = {
|
||||
const gPrefs = new Preferences(PREF_BRANCH);
|
||||
const gPrefsTelemetry = new Preferences(PREF_BRANCH_TELEMETRY);
|
||||
let gExperimentsEnabled = false;
|
||||
let gAddonProvider = null;
|
||||
let gExperiments = null;
|
||||
let gLogAppenderDump = null;
|
||||
let gPolicyCounter = 0;
|
||||
@ -200,8 +206,9 @@ function addonInstallForURL(url, hash) {
|
||||
// experiment addons.
|
||||
function installedExperimentAddons() {
|
||||
let deferred = Promise.defer();
|
||||
AddonManager.getAddonsByTypes(["experiment"],
|
||||
addons => deferred.resolve(addons));
|
||||
AddonManager.getAddonsByTypes(["experiment"], (addons) => {
|
||||
deferred.resolve([a for (a of addons) if (!a.appDisabled)]);
|
||||
});
|
||||
return deferred.promise;
|
||||
}
|
||||
|
||||
@ -462,9 +469,33 @@ Experiments.Experiments.prototype = {
|
||||
|
||||
AddonManager.addAddonListener(this);
|
||||
AddonManager.addInstallListener(this);
|
||||
|
||||
if (!gAddonProvider) {
|
||||
// The properties of this AddonType should be kept in sync with the
|
||||
// experiment AddonType registered in XPIProvider.
|
||||
this._log.trace("Registering previous experiment add-on provider.");
|
||||
gAddonProvider = new Experiments.PreviousExperimentProvider(this, [
|
||||
new AddonManagerPrivate.AddonType("experiment",
|
||||
URI_EXTENSION_STRINGS,
|
||||
STRING_TYPE_NAME,
|
||||
AddonManager.VIEW_TYPE_LIST,
|
||||
11000,
|
||||
AddonManager.TYPE_UI_HIDE_EMPTY),
|
||||
]);
|
||||
AddonManagerPrivate.registerProvider(gAddonProvider);
|
||||
}
|
||||
|
||||
},
|
||||
|
||||
_unregisterWithAddonManager: function () {
|
||||
this._log.trace("Unregistering instance with Addon Manager.");
|
||||
|
||||
if (gAddonProvider) {
|
||||
this._log.trace("Unregistering previous experiment add-on provider.");
|
||||
AddonManagerPrivate.unregisterProvider(gAddonProvider);
|
||||
gAddonProvider = null;
|
||||
}
|
||||
|
||||
AddonManager.removeInstallListener(this);
|
||||
AddonManager.removeAddonListener(this);
|
||||
},
|
||||
@ -711,6 +742,11 @@ Experiments.Experiments.prototype = {
|
||||
return;
|
||||
}
|
||||
|
||||
if (install.addon.appDisabled) {
|
||||
// This is a PreviousExperiment
|
||||
return;
|
||||
}
|
||||
|
||||
// We want to be in control of all experiment add-ons: reject installs
|
||||
// for add-ons that we don't know about.
|
||||
|
||||
@ -1754,7 +1790,14 @@ Experiments.ExperimentEntry.prototype = {
|
||||
|
||||
let deferred = Promise.defer();
|
||||
|
||||
AddonManager.getAddonByID(this._addonId, deferred.resolve);
|
||||
AddonManager.getAddonByID(this._addonId, (addon) => {
|
||||
if (addon && addon.appDisabled) {
|
||||
// Don't return PreviousExperiments.
|
||||
addon = null;
|
||||
}
|
||||
|
||||
deferred.resolve(addon);
|
||||
});
|
||||
|
||||
return deferred.promise;
|
||||
},
|
||||
@ -1957,3 +2000,159 @@ ExperimentsProvider.prototype = Object.freeze({
|
||||
});
|
||||
},
|
||||
});
|
||||
|
||||
/**
|
||||
* An Add-ons Manager provider that knows about old experiments.
|
||||
*
|
||||
* This provider exposes read-only add-ons corresponding to previously-active
|
||||
* experiments. The existence of this provider (and the add-ons it knows about)
|
||||
* facilitates the display of old experiments in the Add-ons Manager UI with
|
||||
* very little custom code in that component.
|
||||
*/
|
||||
this.Experiments.PreviousExperimentProvider = function (experiments) {
|
||||
this._experiments = experiments;
|
||||
}
|
||||
|
||||
this.Experiments.PreviousExperimentProvider.prototype = Object.freeze({
|
||||
startup: function () {},
|
||||
shutdown: function () {},
|
||||
|
||||
getAddonByID: function (id, cb) {
|
||||
this._getPreviousExperiments().then((experiments) => {
|
||||
for (let experiment of experiments) {
|
||||
if (experiment.id == id) {
|
||||
cb(new PreviousExperimentAddon(experiment));
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
cb(null);
|
||||
},
|
||||
(error) => {
|
||||
cb(null);
|
||||
});
|
||||
},
|
||||
|
||||
getAddonsByTypes: function (types, cb) {
|
||||
if (types && types.length > 0 && types.indexOf("experiment") == -1) {
|
||||
cb([]);
|
||||
return;
|
||||
}
|
||||
|
||||
this._getPreviousExperiments().then((experiments) => {
|
||||
cb([new PreviousExperimentAddon(e) for (e of experiments)]);
|
||||
},
|
||||
(error) => {
|
||||
cb([]);
|
||||
});
|
||||
},
|
||||
|
||||
_getPreviousExperiments: function () {
|
||||
return this._experiments.getExperiments().then((experiments) => {
|
||||
return Promise.resolve([e for (e of experiments) if (!e.active)]);
|
||||
});
|
||||
},
|
||||
});
|
||||
|
||||
/**
|
||||
* An add-on that represents a previously-installed experiment.
|
||||
*/
|
||||
function PreviousExperimentAddon(experiment) {
|
||||
this._id = experiment.id;
|
||||
this._name = experiment.name;
|
||||
this._endDate = experiment.endDate;
|
||||
}
|
||||
|
||||
PreviousExperimentAddon.prototype = Object.freeze({
|
||||
// BEGIN REQUIRED ADDON PROPERTIES
|
||||
|
||||
get appDisabled() {
|
||||
return true;
|
||||
},
|
||||
|
||||
get blocklistState() {
|
||||
Ci.nsIBlocklistService.STATE_NOT_BLOCKED
|
||||
},
|
||||
|
||||
get creator() {
|
||||
return new AddonManagerPrivate.AddonAuthor("");
|
||||
},
|
||||
|
||||
get foreignInstall() {
|
||||
return false;
|
||||
},
|
||||
|
||||
get id() {
|
||||
return this._id;
|
||||
},
|
||||
|
||||
get isActive() {
|
||||
return false;
|
||||
},
|
||||
|
||||
get isCompatible() {
|
||||
return true;
|
||||
},
|
||||
|
||||
get isPlatformCompatible() {
|
||||
return true;
|
||||
},
|
||||
|
||||
get name() {
|
||||
return this._name;
|
||||
},
|
||||
|
||||
get pendingOperations() {
|
||||
return AddonManager.PENDING_NONE;
|
||||
},
|
||||
|
||||
get permissions() {
|
||||
return 0;
|
||||
},
|
||||
|
||||
get providesUpdatesSecurely() {
|
||||
return true;
|
||||
},
|
||||
|
||||
get scope() {
|
||||
return AddonManager.SCOPE_PROFILE;
|
||||
},
|
||||
|
||||
get type() {
|
||||
return "experiment";
|
||||
},
|
||||
|
||||
get userDisabled() {
|
||||
return true;
|
||||
},
|
||||
|
||||
get version() {
|
||||
return null;
|
||||
},
|
||||
|
||||
// END REQUIRED PROPERTIES
|
||||
|
||||
// BEGIN OPTIONAL PROPERTIES
|
||||
|
||||
// TODO description
|
||||
|
||||
get updateDate() {
|
||||
return new Date(this._endDate);
|
||||
},
|
||||
|
||||
// END OPTIONAL PROPERTIES
|
||||
|
||||
// BEGIN REQUIRED METHODS
|
||||
|
||||
isCompatibleWith: function (appVersion, platformVersion) {
|
||||
return true;
|
||||
},
|
||||
|
||||
findUpdates: function (listener, reason, appVersion, platformVersion) {
|
||||
AddonManagerPrivate.callNoUpdateListeners(this, listener, reason,
|
||||
appVersion, platformVersion);
|
||||
},
|
||||
|
||||
// END REQUIRED METHODS
|
||||
|
||||
});
|
||||
|
@ -155,10 +155,16 @@ function loadAddonManager() {
|
||||
startupManager();
|
||||
}
|
||||
|
||||
function getExperimentAddons() {
|
||||
function getExperimentAddons(previous=false) {
|
||||
let deferred = Promise.defer();
|
||||
|
||||
AddonManager.getAddonsByTypes(["experiment"], deferred.resolve);
|
||||
AddonManager.getAddonsByTypes(["experiment"], (addons) => {
|
||||
if (previous) {
|
||||
deferred.resolve(addons);
|
||||
} else {
|
||||
deferred.resolve([a for (a of addons) if (!a.appDisabled)]);
|
||||
}
|
||||
});
|
||||
|
||||
return deferred.promise;
|
||||
}
|
||||
|
177
browser/experiments/test/xpcshell/test_previous_provider.js
Normal file
177
browser/experiments/test/xpcshell/test_previous_provider.js
Normal file
@ -0,0 +1,177 @@
|
||||
/* Any copyright is dedicated to the Public Domain.
|
||||
* http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||
|
||||
"use strict";
|
||||
|
||||
Cu.import("resource://gre/modules/Promise.jsm");
|
||||
Cu.import("resource:///modules/experiments/Experiments.jsm");
|
||||
Cu.import("resource://testing-common/httpd.js");
|
||||
|
||||
let gDataRoot;
|
||||
let gHttpServer;
|
||||
let gManifestObject;
|
||||
|
||||
function run_test() {
|
||||
run_next_test();
|
||||
}
|
||||
|
||||
add_task(function test_setup() {
|
||||
loadAddonManager();
|
||||
do_get_profile();
|
||||
|
||||
gHttpServer = new HttpServer();
|
||||
gHttpServer.start(-1);
|
||||
let httpRoot = "http://localhost:" + gHttpServer.identity.primaryPort + "/";
|
||||
gDataRoot = httpRoot + "data/";
|
||||
gHttpServer.registerDirectory("/data/", do_get_cwd());
|
||||
gHttpServer.registerPathHandler("/manifests/handler", (req, res) => {
|
||||
res.setStatusLine(null, 200, "OK");
|
||||
res.write(JSON.stringify(gManifestObject));
|
||||
res.processAsync();
|
||||
res.finish();
|
||||
});
|
||||
do_register_cleanup(() => gHttpServer.stop(() => {}));
|
||||
|
||||
Services.prefs.setBoolPref("experiments.enabled", true);
|
||||
Services.prefs.setCharPref("experiments.manifest.uri",
|
||||
httpRoot + "manifests/handler");
|
||||
Services.prefs.setBoolPref("experiments.logging.dump", true);
|
||||
Services.prefs.setCharPref("experiments.logging.level", "Trace");
|
||||
disableCertificateChecks();
|
||||
});
|
||||
|
||||
add_task(function* test_provider_basic() {
|
||||
let e = Experiments.instance();
|
||||
|
||||
let provider = new Experiments.PreviousExperimentProvider(e);
|
||||
let deferred = Promise.defer();
|
||||
provider.getAddonsByTypes(["experiment"], (addons) => {
|
||||
deferred.resolve(addons);
|
||||
});
|
||||
let addons = yield deferred.promise;
|
||||
Assert.ok(Array.isArray(addons), "getAddonsByTypes returns an Array.");
|
||||
Assert.equal(addons.length, 0, "No previous add-ons returned.");
|
||||
|
||||
gManifestObject = {
|
||||
version: 1,
|
||||
experiments: [
|
||||
{
|
||||
id: EXPERIMENT1_ID,
|
||||
xpiURL: gDataRoot + EXPERIMENT1_XPI_NAME,
|
||||
xpiHash: EXPERIMENT1_XPI_SHA1,
|
||||
startTime: Date.now() / 1000 - 60,
|
||||
endTime: Date.now() / 1000 + 60,
|
||||
maxActiveSeconds: 60,
|
||||
appName: ["XPCShell"],
|
||||
channel: [e._policy.updatechannel()],
|
||||
},
|
||||
],
|
||||
};
|
||||
|
||||
yield e.updateManifest();
|
||||
|
||||
deferred = Promise.defer();
|
||||
provider.getAddonsByTypes(["experiment"], (addons) => {
|
||||
deferred.resolve(addons);
|
||||
});
|
||||
addons = yield deferred.promise;
|
||||
Assert.equal(addons.length, 0, "Still no previous experiment.");
|
||||
|
||||
let experiments = yield e.getExperiments();
|
||||
Assert.equal(experiments.length, 1, "1 experiment present.");
|
||||
Assert.ok(experiments[0].active, "It is active.");
|
||||
|
||||
// Deactivate it.
|
||||
defineNow(e._policy, new Date(gManifestObject.experiments[0].endTime * 1000 + 1000));
|
||||
yield e.updateManifest();
|
||||
|
||||
experiments = yield e.getExperiments();
|
||||
Assert.equal(experiments.length, 1, "1 experiment present.");
|
||||
Assert.equal(experiments[0].active, false, "It isn't active.");
|
||||
|
||||
deferred = Promise.defer();
|
||||
provider.getAddonsByTypes(["experiment"], (addons) => {
|
||||
deferred.resolve(addons);
|
||||
});
|
||||
addons = yield deferred.promise;
|
||||
Assert.equal(addons.length, 1, "1 previous add-on known.");
|
||||
Assert.equal(addons[0].id, EXPERIMENT1_ID, "ID matches expected.");
|
||||
|
||||
deferred = Promise.defer();
|
||||
provider.getAddonByID(EXPERIMENT1_ID, (addon) => {
|
||||
deferred.resolve(addon);
|
||||
});
|
||||
let addon = yield deferred.promise;
|
||||
Assert.ok(addon, "We got an add-on from its ID.");
|
||||
Assert.equal(addon.id, EXPERIMENT1_ID, "ID matches expected.");
|
||||
Assert.ok(addon.appDisabled, "Add-on is a previous experiment.");
|
||||
Assert.ok(addon.userDisabled, "Add-on is disabled.");
|
||||
Assert.equal(addon.type, "experiment", "Add-on is an experiment.");
|
||||
Assert.equal(addon.isActive, false, "Add-on is not active.");
|
||||
Assert.equal(addon.permissions, 0, "Add-on has no permissions.");
|
||||
|
||||
deferred = Promise.defer();
|
||||
AddonManager.getAddonsByTypes(["experiment"], (addons) => {
|
||||
deferred.resolve(addons);
|
||||
});
|
||||
addons = yield deferred.promise;
|
||||
Assert.equal(addons.length, 1, "Got 1 experiment from add-on manager.");
|
||||
Assert.equal(addons[0].id, EXPERIMENT1_ID, "ID matches expected.");
|
||||
Assert.ok(addons[0].appDisabled, "It is a previous experiment add-on.");
|
||||
});
|
||||
|
||||
add_task(function* test_active_and_previous() {
|
||||
// Building on the previous test, activate experiment 2.
|
||||
let e = Experiments.instance();
|
||||
let provider = new Experiments.PreviousExperimentProvider(e);
|
||||
|
||||
gManifestObject = {
|
||||
version: 1,
|
||||
experiments: [
|
||||
{
|
||||
id: EXPERIMENT2_ID,
|
||||
xpiURL: gDataRoot + EXPERIMENT2_XPI_NAME,
|
||||
xpiHash: EXPERIMENT2_XPI_SHA1,
|
||||
startTime: Date.now() / 1000 - 60,
|
||||
endTime: Date.now() / 1000 + 60,
|
||||
maxActiveSeconds: 60,
|
||||
appName: ["XPCShell"],
|
||||
channel: [e._policy.updatechannel()],
|
||||
},
|
||||
],
|
||||
};
|
||||
|
||||
defineNow(e._policy, new Date());
|
||||
yield e.updateManifest();
|
||||
|
||||
let experiments = yield e.getExperiments();
|
||||
Assert.equal(experiments.length, 2, "2 experiments known.");
|
||||
|
||||
let deferred = Promise.defer();
|
||||
provider.getAddonsByTypes(["experiment"], (addons) => {
|
||||
deferred.resolve(addons);
|
||||
});
|
||||
let addons = yield deferred.promise;
|
||||
Assert.equal(addons.length, 1, "1 previous experiment.");
|
||||
|
||||
deferred = Promise.defer();
|
||||
AddonManager.getAddonsByTypes(["experiment"], (addons) => {
|
||||
deferred.resolve(addons);
|
||||
});
|
||||
addons = yield deferred.promise;
|
||||
Assert.equal(addons.length, 2, "2 experiment add-ons known.");
|
||||
|
||||
for (let addon of addons) {
|
||||
if (addon.id == EXPERIMENT1_ID) {
|
||||
Assert.equal(addon.isActive, false, "Add-on is not active.");
|
||||
Assert.ok(addon.appDisabled, "Should be a previous experiment.");
|
||||
}
|
||||
else if (addon.id == EXPERIMENT2_ID) {
|
||||
Assert.ok(addon.isActive, "Add-on is active.");
|
||||
Assert.ok(!addon.appDisabled, "Should not be a previous experiment.");
|
||||
}
|
||||
else {
|
||||
throw new Error("Unexpected add-on ID: " + addon.id);
|
||||
}
|
||||
}
|
||||
});
|
@ -20,3 +20,4 @@ generated-files =
|
||||
[test_fetch.js]
|
||||
[test_telemetry.js]
|
||||
[test_healthreport.js]
|
||||
[test_previous_provider.js]
|
||||
|
@ -260,12 +260,50 @@ add_task(function testDeactivateExperiment() {
|
||||
return;
|
||||
}
|
||||
|
||||
// Fake an empty manifest to purge data from previous manifest.
|
||||
yield gExperiments._updateExperiments({
|
||||
"version": 1,
|
||||
"experiments": [],
|
||||
});
|
||||
|
||||
yield gExperiments.disableExperiment("testing");
|
||||
|
||||
// We should have a record of the previously-active experiment.
|
||||
let experiments = yield gExperiments.getExperiments();
|
||||
Assert.equal(experiments.length, 1, "1 experiment is known.");
|
||||
Assert.equal(experiments[0].active, false, "Experiment is not active.");
|
||||
|
||||
// We should have a previous experiment in the add-ons manager.
|
||||
let deferred = Promise.defer();
|
||||
AddonManager.getAddonsByTypes(["experiment"], (addons) => {
|
||||
deferred.resolve(addons);
|
||||
});
|
||||
let addons = yield deferred.promise;
|
||||
Assert.equal(addons.length, 1, "1 experiment add-on known.");
|
||||
Assert.ok(addons[0].appDisabled, "It is a previous experiment.");
|
||||
Assert.equal(addons[0].id, "experiment-1", "Add-on ID matches expected.");
|
||||
|
||||
// Verify the UI looks sane.
|
||||
// TODO remove the pane cycle once the UI refreshes automatically.
|
||||
yield gCategoryUtilities.openType("extension");
|
||||
|
||||
Assert.ok(gCategoryUtilities.isTypeVisible("experiment"), "Experiment tab visible.");
|
||||
yield gCategoryUtilities.openType("experiment");
|
||||
|
||||
let item = get_addon_element(gManagerWindow, "experiment-1");
|
||||
Assert.ok(item, "Got add-on element.");
|
||||
Assert.ok(!item.active, "Element should not be active.");
|
||||
|
||||
// User control buttons should not be present because previous experiments
|
||||
// should have no permissions.
|
||||
let el = item.ownerDocument.getAnonymousElementByAttribute(item, "anonid", "remove-btn");
|
||||
is_element_hidden(el, "Remove button is not visible.");
|
||||
el = item.ownerDocument.getAnonymousElementByAttribute(item, "anonid", "disable-btn");
|
||||
is_element_hidden(el, "Disable button is not visible.");
|
||||
el = item.ownerDocument.getAnonymousElementByAttribute(item, "anonid", "enable-btn");
|
||||
is_element_hidden(el, "Enable button is not visible.");
|
||||
el = item.ownerDocument.getAnonymousElementByAttribute(item, "anonid", "preferences-btn");
|
||||
is_element_hidden(el, "Preferences button is not visible.");
|
||||
});
|
||||
|
||||
add_task(function* testCleanup() {
|
||||
@ -285,6 +323,4 @@ add_task(function* testCleanup() {
|
||||
Assert.equal(addons.length, 0, "No experiment add-ons are installed.");
|
||||
|
||||
yield close_manager(gManagerWindow);
|
||||
|
||||
});
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user