Backed out 3 changesets (bug 1358907) for devtools failures in leakcheck a=backout

Backed out changeset 529eccc2bc69 (bug 1358907)
Backed out changeset 5368cc2c4f38 (bug 1358907)
Backed out changeset d112bc46842f (bug 1358907)

MozReview-Commit-ID: D2oHpM7GgrW
This commit is contained in:
Wes Kocher 2017-07-03 14:43:57 -07:00
parent a85cdee020
commit 4572107fe1
20 changed files with 80 additions and 200 deletions

View File

@ -164,30 +164,14 @@ function addonInstallForURL(url, hash) {
// Returns a promise that is resolved with an Array<Addon> of the installed
// experiment addons.
function installedExperimentAddons() {
return AddonManager.getActiveAddons(["experiment"]).then(addons => {
return AddonManager.getAddonsByTypes(["experiment"]).then(addons => {
return addons.filter(a => !a.appDisabled);
});
}
// Takes an Array<Addon> and returns a promise that is resolved when the
// addons are uninstalled.
async function uninstallAddons(addons) {
if (!AddonManagerPrivate.isDBLoaded()) {
await new Promise(resolve => {
Services.obs.addObserver({
observe(subject, topic, data) {
Services.obs.removeObserver(this, "xpi-database-loaded");
resolve();
},
}, "xpi-database-loaded");
});
// This function was called during startup so the addons that were
// passed in were partial addon objects. Now that the full addons
// database is loaded, get proper Addon objects.
addons = await AddonManager.getAddonsByIDs(addons.map(a => a.id));
}
function uninstallAddons(addons) {
let ids = new Set(addons.map(addon => addon.id));
return new Promise(resolve => {
@ -207,6 +191,10 @@ async function uninstallAddons(addons) {
AddonManager.addAddonListener(listener);
for (let addon of addons) {
// Disabling the add-on before uninstalling is necessary to cause tests to
// pass. This might be indicative of a bug in XPIProvider.
// TODO follow up in bug 992396.
addon.userDisabled = true;
addon.uninstall();
}

View File

@ -155,7 +155,6 @@ function startAddonManagerOnly() {
.getService(Ci.nsIObserver)
.QueryInterface(Ci.nsITimerCallback);
addonManager.observe(null, "addons-startup", null);
Services.obs.notifyObservers(null, "sessionstore-windows-restored");
}
function getExperimentAddons(previous = false) {

View File

@ -22,12 +22,11 @@ Cu.import("resource://gre/modules/AppConstants.jsm");
const Utils = TelemetryUtils;
const { AddonManager, AddonManagerPrivate } = Cu.import("resource://gre/modules/AddonManager.jsm", {});
XPCOMUtils.defineLazyModuleGetter(this, "AttributionCode",
"resource:///modules/AttributionCode.jsm");
XPCOMUtils.defineLazyModuleGetter(this, "ctypes",
"resource://gre/modules/ctypes.jsm");
Cu.import("resource://gre/modules/AddonManager.jsm");
XPCOMUtils.defineLazyModuleGetter(this, "LightweightThemeManager",
"resource://gre/modules/LightweightThemeManager.jsm");
XPCOMUtils.defineLazyModuleGetter(this, "ProfileAge",
@ -483,34 +482,13 @@ EnvironmentAddonBuilder.prototype = {
return Promise.reject(err);
}
this._pendingTask = (async () => {
try {
// Gather initial addons details
await this._updateAddons();
if (!AddonManagerPrivate.isDBLoaded()) {
// The addon database has not been loaded, so listen for the event
// triggered by the AddonManager when it is loaded so we can
// immediately gather full data at that time.
await new Promise(resolve => {
const ADDON_LOAD_NOTIFICATION = "xpi-database-loaded";
Services.obs.addObserver({
observe(subject, topic, data) {
Services.obs.removeObserver(this, ADDON_LOAD_NOTIFICATION);
resolve();
},
}, ADDON_LOAD_NOTIFICATION);
});
// Now gather complete addons details.
await this._updateAddons();
}
} catch (err) {
this._pendingTask = this._updateAddons().then(
() => { this._pendingTask = null; },
(err) => {
this._environment._log.error("init - Exception in _updateAddons", err);
} finally {
this._pendingTask = null;
}
})();
);
return this._pendingTask;
},
@ -573,12 +551,6 @@ EnvironmentAddonBuilder.prototype = {
AddonManager.removeAddonListener(this);
Services.obs.removeObserver(this, EXPERIMENTS_CHANGED_TOPIC);
}
// At startup, _pendingTask is set to a Promise that does not resolve
// until the addons database has been read so complete details about
// addons are available. Returning it here will cause it to block
// profileBeforeChange, guranteeing that full information will be
// available by the time profileBeforeChangeTelemetry is fired.
return this._pendingTask;
},
@ -629,43 +601,44 @@ EnvironmentAddonBuilder.prototype = {
*/
async _getActiveAddons() {
// Request addons, asynchronously.
let allAddons = await AddonManager.getActiveAddons(["extension", "service"]);
let allAddons = await AddonManager.getAddonsByTypes(["extension", "service"]);
let isDBLoaded = AddonManagerPrivate.isDBLoaded();
let activeAddons = {};
for (let addon of allAddons) {
// Skip addons which are not active.
if (!addon.isActive) {
continue;
}
// Weird addon data in the wild can lead to exceptions while collecting
// the data.
try {
// Make sure to have valid dates.
let installDate = new Date(Math.max(0, addon.installDate));
let updateDate = new Date(Math.max(0, addon.updateDate));
activeAddons[addon.id] = {
blocklisted: (addon.blocklistState !== Ci.nsIBlocklistService.STATE_NOT_BLOCKED),
description: limitStringToLength(addon.description, MAX_ADDON_STRING_LENGTH),
name: limitStringToLength(addon.name, MAX_ADDON_STRING_LENGTH),
userDisabled: enforceBoolean(addon.userDisabled),
appDisabled: addon.appDisabled,
version: limitStringToLength(addon.version, MAX_ADDON_STRING_LENGTH),
scope: addon.scope,
type: addon.type,
foreignInstall: enforceBoolean(addon.foreignInstall),
hasBinaryComponents: addon.hasBinaryComponents,
installDay: Utils.millisecondsToDays(installDate.getTime()),
updateDay: Utils.millisecondsToDays(updateDate.getTime()),
signedState: addon.signedState,
isSystem: addon.isSystem,
isWebExtension: addon.isWebExtension,
multiprocessCompatible: Boolean(addon.multiprocessCompatible),
};
// getActiveAddons() gives limited data during startup and full
// data after the addons database is loaded.
if (isDBLoaded) {
let installDate = new Date(Math.max(0, addon.installDate));
Object.assign(activeAddons[addon.id], {
blocklisted: (addon.blocklistState !== Ci.nsIBlocklistService.STATE_NOT_BLOCKED),
description: limitStringToLength(addon.description, MAX_ADDON_STRING_LENGTH),
name: limitStringToLength(addon.name, MAX_ADDON_STRING_LENGTH),
userDisabled: enforceBoolean(addon.userDisabled),
appDisabled: addon.appDisabled,
foreignInstall: enforceBoolean(addon.foreignInstall),
hasBinaryComponents: addon.hasBinaryComponents,
installDay: Utils.millisecondsToDays(installDate.getTime()),
signedState: addon.signedState,
});
}
if (addon.signedState !== undefined)
activeAddons[addon.id].signedState = addon.signedState;
} catch (ex) {
this._environment._log.error("_getActiveAddons - An addon was discarded due to an error", ex);
continue;
@ -681,7 +654,7 @@ EnvironmentAddonBuilder.prototype = {
*/
async _getActiveTheme() {
// Request themes, asynchronously.
let themes = await AddonManager.getActiveAddons(["theme"]);
let themes = await AddonManager.getAddonsByTypes(["theme"]);
let activeTheme = {};
// We only store information about the active theme.

View File

@ -215,7 +215,7 @@ Structure:
scope: <integer>,
type: <string>, // "extension", "service", ...
foreignInstall: <bool>,
hasBinaryComponents: <bool>,
hasBinaryComponents: <bool>
installDay: <number>, // days since UNIX epoch, 0 on failure
updateDay: <number>, // days since UNIX epoch, 0 on failure
signedState: <integer>, // whether the add-on is signed by AMO, only present for extensions
@ -395,8 +395,6 @@ activeAddons
Starting from Firefox 44, the length of the following string fields: ``name``, ``description`` and ``version`` is limited to 100 characters. The same limitation applies to the same fields in ``theme`` and ``activePlugins``.
Some of the fields in the record for each addon are not available during startup. The fields that will always be present are ``id``, ``version``, ``type``, ``updateDate``, ``scope``, ``isSystem``, ``isWebExtension``, and ``multiprocessCompatible``. All the other fields documented above become present shortly after the ``sessionstore-windows-restored`` event is dispatched.
experiments
-----------
For each experiment we collect the ``id`` and the ``branch`` the client is enrolled in. Both fields are truncated to 100 characters and a warning is printed when that happens. This section will eventually supersede ``addons/activeExperiment``.

View File

@ -179,10 +179,6 @@ function loadAddonManager(...args) {
return AddonTestUtils.promiseStartupManager();
}
function finishAddonManagerStartup() {
Services.obs.notifyObservers(null, "test-load-xpi-database");
}
var gAppInfo = null;
function createAppInfo(ID = "xpcshell@tests.mozilla.org", name = "XPCShell",

View File

@ -66,7 +66,6 @@ add_task(async function() {
// Setup.
do_get_profile(true);
loadAddonManager(APP_ID, APP_NAME, APP_VERSION, PLATFORM_VERSION);
finishAddonManagerStartup();
Services.prefs.setBoolPref(PREF_TELEMETRY_ENABLED, true);
await TelemetryController.testSetup();
// Make sure we don't generate unexpected pings due to pref changes.

View File

@ -91,7 +91,6 @@ add_task(async function() {
// Setup.
do_get_profile(true);
loadAddonManager(APP_ID, APP_NAME, APP_VERSION, PLATFORM_VERSION);
finishAddonManagerStartup();
Services.prefs.setBoolPref(PREF_TELEMETRY_ENABLED, true);
await TelemetryController.testSetup();
if (runningInParent) {

View File

@ -140,7 +140,6 @@ add_task(async function() {
// Setup.
do_get_profile(true);
loadAddonManager(APP_ID, APP_NAME, APP_VERSION, PLATFORM_VERSION);
finishAddonManagerStartup();
Services.prefs.setBoolPref(PREF_TELEMETRY_ENABLED, true);
await TelemetryController.testSetup();
if (runningInParent) {

View File

@ -88,7 +88,6 @@ add_task(async function test_setup() {
// Addon manager needs a profile directory
do_get_profile();
loadAddonManager("xpcshell@tests.mozilla.org", "XPCShell", "1", "1.9.2");
finishAddonManagerStartup();
// Make sure we don't generate unexpected pings due to pref changes.
await setEmptyPrefWatchlist();

View File

@ -98,7 +98,6 @@ add_task(async function test_setup() {
// Addon manager needs a profile directory
do_get_profile();
loadAddonManager("xpcshell@tests.mozilla.org", "XPCShell", "1", "1.9.2");
finishAddonManagerStartup();
// Make sure we don't generate unexpected pings due to pref changes.
await setEmptyPrefWatchlist();

View File

@ -29,7 +29,6 @@ add_task(async function test_setup() {
// Addon manager needs a profile directory
do_get_profile();
loadAddonManager("xpcshell@tests.mozilla.org", "XPCShell", "1", "1.9.2");
finishAddonManagerStartup();
// Make sure we don't generate unexpected pings due to pref changes.
await setEmptyPrefWatchlist();

View File

@ -1,12 +1,10 @@
/* Any copyright is dedicated to the Public Domain.
* http://creativecommons.org/publicdomain/zero/1.0/ */
const {AddonManager, AddonManagerPrivate} = Cu.import("resource://gre/modules/AddonManager.jsm", {});
Cu.import("resource://gre/modules/AddonManager.jsm");
Cu.import("resource://gre/modules/TelemetryEnvironment.jsm", this);
Cu.import("resource://gre/modules/ObjectUtils.jsm");
Cu.import("resource://gre/modules/Preferences.jsm", this);
Cu.import("resource://gre/modules/PromiseUtils.jsm", this);
Cu.import("resource://gre/modules/Timer.jsm", this);
Cu.import("resource://gre/modules/XPCOMUtils.jsm", this);
Cu.import("resource://testing-common/AddonManagerTesting.jsm");
Cu.import("resource://testing-common/httpd.js");
@ -659,48 +657,38 @@ function checkSystemSection(data) {
} catch (e) {}
}
function checkActiveAddon(data, partialRecord) {
function checkActiveAddon(data) {
let signedState = mozinfo.addon_signing ? "number" : "undefined";
// system add-ons have an undefined signState
if (data.isSystem)
signedState = "undefined";
const EXPECTED_ADDON_FIELDS_TYPES = {
blocklisted: "boolean",
name: "string",
userDisabled: "boolean",
appDisabled: "boolean",
version: "string",
scope: "number",
type: "string",
foreignInstall: "boolean",
hasBinaryComponents: "boolean",
installDay: "number",
updateDay: "number",
signedState,
isSystem: "boolean",
isWebExtension: "boolean",
multiprocessCompatible: "boolean",
};
const FULL_ADDON_FIELD_TYPES = {
blocklisted: "boolean",
name: "string",
userDisabled: "boolean",
appDisabled: "boolean",
foreignInstall: "boolean",
hasBinaryComponents: "boolean",
installDay: "number",
signedState,
};
let fields = EXPECTED_ADDON_FIELDS_TYPES;
if (!partialRecord) {
fields = Object.assign({}, fields, FULL_ADDON_FIELD_TYPES);
for (let f in EXPECTED_ADDON_FIELDS_TYPES) {
Assert.ok(f in data, f + " must be available.");
Assert.equal(typeof data[f], EXPECTED_ADDON_FIELDS_TYPES[f],
f + " must have the correct type.");
}
for (let [name, type] of Object.entries(fields)) {
Assert.ok(name in data, name + " must be available.");
Assert.equal(typeof data[name], type,
name + " must have the correct type.");
}
if (!partialRecord) {
// We check "description" separately, as it can be null.
Assert.ok(checkNullOrString(data.description));
}
// We check "description" separately, as it can be null.
Assert.ok(checkNullOrString(data.description));
}
function checkPlugin(data) {
@ -760,7 +748,7 @@ function checkActiveGMPlugin(data) {
Assert.equal(typeof data.applyBackgroundUpdates, "number");
}
function checkAddonsSection(data, expectBrokenAddons, partialAddonsRecords) {
function checkAddonsSection(data, expectBrokenAddons) {
const EXPECTED_FIELDS = [
"activeAddons", "theme", "activePlugins", "activeGMPlugins", "activeExperiment",
"persona",
@ -775,7 +763,7 @@ function checkAddonsSection(data, expectBrokenAddons, partialAddonsRecords) {
if (!expectBrokenAddons) {
let activeAddons = data.addons.activeAddons;
for (let addon in activeAddons) {
checkActiveAddon(activeAddons[addon], partialAddonsRecords);
checkActiveAddon(activeAddons[addon]);
}
}
@ -827,12 +815,7 @@ function checkExperimentsSection(data) {
}
}
function checkEnvironmentData(data, options = {}) {
const {
isInitial = false,
expectBrokenAddons = false,
} = options;
function checkEnvironmentData(data, isInitial = false, expectBrokenAddons = false) {
checkBuildSection(data);
checkSettingsSection(data);
checkProfileSection(data);
@ -860,14 +843,6 @@ add_task(async function setup() {
// Spoof the persona ID.
LightweightThemeManager.currentTheme =
spoofTheme(PERSONA_ID, PERSONA_NAME, PERSONA_DESCRIPTION);
// The test runs in a fresh profile so starting the AddonManager causes
// the addons database to be created (as does setting new theme).
// For test_addonsStartup below, we want to test a "warm" startup where
// there is already a database on disk. Simulate that here by just
// restarting the AddonManager.
await AddonTestUtils.promiseRestartManager();
// Register a fake plugin host for consistent flash version data.
registerFakePluginHost();
@ -898,19 +873,8 @@ add_task(async function setup() {
});
add_task(async function test_checkEnvironment() {
// During startup we have partial addon records.
// First make sure we haven't yet read the addons DB, then test that
// we have some partial addons data.
Assert.equal(AddonManagerPrivate.isDBLoaded(), false,
"addons database is not loaded");
checkAddonsSection(TelemetryEnvironment.currentEnvironment, false, true);
// Now continue with startup.
let initPromise = TelemetryEnvironment.onInitialized();
finishAddonManagerStartup();
let environmentData = await initPromise;
checkEnvironmentData(environmentData, {isInitial: true});
let environmentData = await TelemetryEnvironment.onInitialized();
checkEnvironmentData(environmentData, true);
spoofPartnerInfo();
Services.obs.notifyObservers(null, DISTRIBUTION_CUSTOMIZATION_COMPLETE_TOPIC);
@ -1448,7 +1412,7 @@ add_task(async function test_collectionWithbrokenAddonData() {
// Check that the new environment contains the Social addon installed with the broken
// manifest and the rest of the data.
let data = TelemetryEnvironment.currentEnvironment;
checkEnvironmentData(data, {expectBrokenAddons: true});
checkEnvironmentData(data, false, true /* expect broken addons*/);
let activeAddons = data.addons.activeAddons;
Assert.ok(BROKEN_ADDON_ID in activeAddons,
@ -1470,8 +1434,7 @@ add_task(async function test_collectionWithbrokenAddonData() {
add_task(async function test_defaultSearchEngine() {
// Check that no default engine is in the environment before the search service is
// initialized.
let data = await TelemetryEnvironment.testCleanRestart().onInitialized();
let data = TelemetryEnvironment.currentEnvironment;
checkEnvironmentData(data);
Assert.ok(!("defaultSearchEngine" in data.settings));
Assert.ok(!("defaultSearchEngineData" in data.settings));

View File

@ -60,7 +60,6 @@ add_task(async function test_setup() {
// Addon manager needs a profile directory
do_get_profile(true);
loadAddonManager("xpcshell@tests.mozilla.org", "XPCShell", "1", "1.9.2");
finishAddonManagerStartup();
// Make sure we don't generate unexpected pings due to pref changes.
await setEmptyPrefWatchlist();

View File

@ -146,7 +146,6 @@ add_task(async function test_setup() {
PingServer.registerPingHandler(pingHandler);
do_get_profile();
loadAddonManager("xpcshell@tests.mozilla.org", "XPCShell", "1", "1.9.2");
finishAddonManagerStartup();
// Make sure we don't generate unexpected pings due to pref changes.
await setEmptyPrefWatchlist();

View File

@ -480,7 +480,6 @@ add_task(async function test_setup() {
// Addon manager needs a profile directory
do_get_profile();
loadAddonManager(APP_ID, APP_NAME, APP_VERSION, PLATFORM_VERSION);
finishAddonManagerStartup();
// Make sure we don't generate unexpected pings due to pref changes.
await setEmptyPrefWatchlist();

View File

@ -10,7 +10,6 @@ add_task(async function test_setup() {
// Addon manager needs a profile directory
do_get_profile();
loadAddonManager("xpcshell@tests.mozilla.org", "XPCShell", "1", "1.9.2");
finishAddonManagerStartup();
// Make sure we don't generate unexpected pings due to pref changes.
await setEmptyPrefWatchlist();

View File

@ -24,7 +24,6 @@ function getSimpleMeasurementsFromTelemetryController() {
add_task(async function test_setup() {
// Telemetry needs the AddonManager.
loadAddonManager();
finishAddonManagerStartup();
// Make profile available for |TelemetryController.testShutdown()|.
do_get_profile();

View File

@ -2325,25 +2325,16 @@ this.XPIProvider = {
// XPI database so that the telemetry environment can be populated
// with detailed addon information.
if (!this.isDBLoaded) {
// The test-load-xpi-database event is only triggered from
// tests, and only as a temporary workaround for bug 1372845.
// This can be cleaned up when that bug is resolved.
const EVENTS = [ "sessionstore-windows-restored", "test-load-xpi-database" ];
let observer = {
Services.obs.addObserver({
observe(subject, topic, data) {
for (let event of EVENTS) {
Services.obs.removeObserver(this, event);
}
Services.obs.removeObserver(this, "sessionstore-windows-restored");
// It would be nice to defer some of the work here until we
// have idle time but we can't yet use requestIdleCallback()
// from chrome. See bug 1358476.
XPIDatabase.asyncLoadDB();
},
};
for (let event of EVENTS) {
Services.obs.addObserver(observer, event);
}
}, "sessionstore-windows-restored");
}
AddonManagerPrivate.recordTimestamp("XPI_startup_end");
@ -3672,9 +3663,6 @@ this.XPIProvider = {
let result = [];
for (let addon of XPIStates.enabledAddons()) {
if (aTypes && !aTypes.includes(addon.type)) {
continue;
}
let location = this.installLocationsByName[addon.location.name];
let scope, isSystem;
if (location) {

View File

@ -658,7 +658,7 @@ this.XPIDatabase = {
* flush after the database is flushed and
* all cleanup is done
*/
async shutdown() {
shutdown() {
logger.debug("shutdown");
if (this.initialized) {
// If our last database I/O had an error, try one last time to save.
@ -676,33 +676,28 @@ this.XPIDatabase = {
"XPIDB_saves_late", this._deferredSave.dirty ? 1 : 0);
}
// If we're shutting down while still loading, finish loading
// before everything else!
if (this._dbPromise) {
await this._dbPromise;
}
// Await and pending DB writes and finish cleaning up.
try {
await this.flush();
} catch (error) {
logger.error("Flush of XPI database failed", error);
AddonManagerPrivate.recordSimpleMeasure("XPIDB_shutdownFlush_failed", 1);
// If our last attempt to read or write the DB failed, force a new
// extensions.ini to be written to disk on the next startup
Services.prefs.setBoolPref(PREF_PENDING_OPERATIONS, true);
throw error;
}
// Clear out the cached addons data loaded from JSON
delete this.addonDB;
delete this._dbPromise;
// same for the deferred save
delete this._deferredSave;
// re-enable the schema version setter
delete this._schemaVersionSet;
// Return a promise that any pending writes of the DB are complete and we
// are finished cleaning up
let flushPromise = this.flush();
flushPromise.catch(error => {
logger.error("Flush of XPI database failed", error);
AddonManagerPrivate.recordSimpleMeasure("XPIDB_shutdownFlush_failed", 1);
// If our last attempt to read or write the DB failed, force a new
// extensions.ini to be written to disk on the next startup
Services.prefs.setBoolPref(PREF_PENDING_OPERATIONS, true);
})
.then(count => {
// Clear out the cached addons data loaded from JSON
delete this.addonDB;
delete this._dbPromise;
// same for the deferred save
delete this._deferredSave;
// re-enable the schema version setter
delete this._schemaVersionSet;
});
return flushPromise;
}
return Promise.resolve(0);
},
/**

View File

@ -255,15 +255,6 @@ function run_test_1() {
gExtensionsJSON.create(AM_Ci.nsIFile.DIRECTORY_TYPE, FileUtils.PERMS_DIRECTORY);
startupManager(false);
// Load the database.
awaitPromise(new Promise(resolve => {
Services.obs.addObserver(function listener() {
Services.obs.removeObserver(listener, "xpi-database-loaded");
resolve();
}, "xpi-database-loaded");
Services.obs.notifyObservers(null, "sessionstore-windows-restored");
}));
// Accessing the add-ons should open and recover the database
AddonManager.getAddonsByIDs(["addon1@tests.mozilla.org",
"addon2@tests.mozilla.org",