зеркало из https://github.com/mozilla/gecko-dev.git
Bug 1192921: Add an install location for system add-ons. r=rhelmer
This adds two new directory install locations. One contains the default system add-ons that ship with the application, the other contains system add-on that will eventually be updatable at runtime. The updatable location tracks the expected list of add-ons in a pref. and only returns add-ons from that list when asked for its list of add-ons. After processFileChanges has scanned all add-ons and updated the database it checks if the updated system add-ons match the expected set. If not we ignore those add-ons when working out which add-ons should be visible. If they do match then we ignore the app-shipped system add-ons when working out which are visible. --HG-- extra : commitid : H1px7lWsLNj extra : rebase_source : 6b70fd6c88608169e2974e19acd37bf63243c1cd
This commit is contained in:
Родитель
72d129f6ff
Коммит
a486e9c783
|
@ -99,6 +99,7 @@ const PREF_INSTALL_DISTRO_ADDONS = "extensions.installDistroAddons";
|
||||||
const PREF_BRANCH_INSTALLED_ADDON = "extensions.installedDistroAddon.";
|
const PREF_BRANCH_INSTALLED_ADDON = "extensions.installedDistroAddon.";
|
||||||
const PREF_SHOWN_SELECTION_UI = "extensions.shownSelectionUI";
|
const PREF_SHOWN_SELECTION_UI = "extensions.shownSelectionUI";
|
||||||
const PREF_INTERPOSITION_ENABLED = "extensions.interposition.enabled";
|
const PREF_INTERPOSITION_ENABLED = "extensions.interposition.enabled";
|
||||||
|
const PREF_SYSTEM_ADDON_SET = "extensions.systemAddonSet";
|
||||||
|
|
||||||
const PREF_EM_MIN_COMPAT_APP_VERSION = "extensions.minCompatibleAppVersion";
|
const PREF_EM_MIN_COMPAT_APP_VERSION = "extensions.minCompatibleAppVersion";
|
||||||
const PREF_EM_MIN_COMPAT_PLATFORM_VERSION = "extensions.minCompatiblePlatformVersion";
|
const PREF_EM_MIN_COMPAT_PLATFORM_VERSION = "extensions.minCompatiblePlatformVersion";
|
||||||
|
@ -116,6 +117,7 @@ const URI_EXTENSION_STRINGS = "chrome://mozapps/locale/extensions/exte
|
||||||
const STRING_TYPE_NAME = "type.%ID%.name";
|
const STRING_TYPE_NAME = "type.%ID%.name";
|
||||||
|
|
||||||
const DIR_EXTENSIONS = "extensions";
|
const DIR_EXTENSIONS = "extensions";
|
||||||
|
const DIR_SYSTEM_ADDONS = "features";
|
||||||
const DIR_STAGE = "staged";
|
const DIR_STAGE = "staged";
|
||||||
const DIR_TRASH = "trash";
|
const DIR_TRASH = "trash";
|
||||||
|
|
||||||
|
@ -131,6 +133,8 @@ const KEY_TEMPDIR = "TmpD";
|
||||||
const KEY_APP_DISTRIBUTION = "XREAppDist";
|
const KEY_APP_DISTRIBUTION = "XREAppDist";
|
||||||
|
|
||||||
const KEY_APP_PROFILE = "app-profile";
|
const KEY_APP_PROFILE = "app-profile";
|
||||||
|
const KEY_APP_SYSTEM_ADDONS = "app-system-addons";
|
||||||
|
const KEY_APP_SYSTEM_DEFAULTS = "app-system-defaults";
|
||||||
const KEY_APP_GLOBAL = "app-global";
|
const KEY_APP_GLOBAL = "app-global";
|
||||||
const KEY_APP_SYSTEM_LOCAL = "app-system-local";
|
const KEY_APP_SYSTEM_LOCAL = "app-system-local";
|
||||||
const KEY_APP_SYSTEM_SHARE = "app-system-share";
|
const KEY_APP_SYSTEM_SHARE = "app-system-share";
|
||||||
|
@ -1957,7 +1961,7 @@ this.XPIStates = {
|
||||||
|
|
||||||
for (let location of XPIProvider.installLocations) {
|
for (let location of XPIProvider.installLocations) {
|
||||||
// The list of add-on like file/directory names in the install location.
|
// The list of add-on like file/directory names in the install location.
|
||||||
let addons = location.addonLocations;
|
let addons = location.getAddonLocations();
|
||||||
// The results of scanning this location.
|
// The results of scanning this location.
|
||||||
let foundAddons = new SerializableMap();
|
let foundAddons = new SerializableMap();
|
||||||
|
|
||||||
|
@ -2313,6 +2317,28 @@ this.XPIProvider = {
|
||||||
XPIProvider.installLocationsByName[location.name] = location;
|
XPIProvider.installLocationsByName[location.name] = location;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function addSystemAddonInstallLocation(aName, aKey, aPaths, aScope) {
|
||||||
|
try {
|
||||||
|
var dir = FileUtils.getDir(aKey, aPaths);
|
||||||
|
}
|
||||||
|
catch (e) {
|
||||||
|
// Some directories aren't defined on some platforms, ignore them
|
||||||
|
logger.debug("Skipping unavailable install location " + aName);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
var location = new SystemAddonInstallLocation(aName, dir, aScope, aAppChanged !== false);
|
||||||
|
}
|
||||||
|
catch (e) {
|
||||||
|
logger.warn("Failed to add system add-on install location " + aName, e);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
XPIProvider.installLocations.push(location);
|
||||||
|
XPIProvider.installLocationsByName[location.name] = location;
|
||||||
|
}
|
||||||
|
|
||||||
function addRegistryInstallLocation(aName, aRootkey, aScope) {
|
function addRegistryInstallLocation(aName, aRootkey, aScope) {
|
||||||
try {
|
try {
|
||||||
var location = new WinRegInstallLocation(aName, aRootkey, aScope);
|
var location = new WinRegInstallLocation(aName, aRootkey, aScope);
|
||||||
|
@ -2355,6 +2381,14 @@ this.XPIProvider = {
|
||||||
[DIR_EXTENSIONS],
|
[DIR_EXTENSIONS],
|
||||||
AddonManager.SCOPE_PROFILE, false);
|
AddonManager.SCOPE_PROFILE, false);
|
||||||
|
|
||||||
|
addSystemAddonInstallLocation(KEY_APP_SYSTEM_ADDONS, KEY_PROFILEDIR,
|
||||||
|
[DIR_SYSTEM_ADDONS],
|
||||||
|
AddonManager.SCOPE_PROFILE);
|
||||||
|
|
||||||
|
addDirectoryInstallLocation(KEY_APP_SYSTEM_DEFAULTS, KEY_APP_DISTRIBUTION,
|
||||||
|
[DIR_SYSTEM_ADDONS],
|
||||||
|
AddonManager.SCOPE_PROFILE, true);
|
||||||
|
|
||||||
if (enabledScopes & AddonManager.SCOPE_USER) {
|
if (enabledScopes & AddonManager.SCOPE_USER) {
|
||||||
addDirectoryInstallLocation(KEY_APP_SYSTEM_USER, "XREUSysExt",
|
addDirectoryInstallLocation(KEY_APP_SYSTEM_USER, "XREUSysExt",
|
||||||
[Services.appinfo.ID],
|
[Services.appinfo.ID],
|
||||||
|
@ -6757,7 +6791,7 @@ function DirectoryInstallLocation(aName, aDirectory, aScope) {
|
||||||
this._IDToFileMap = {};
|
this._IDToFileMap = {};
|
||||||
this._linkedAddons = [];
|
this._linkedAddons = [];
|
||||||
|
|
||||||
if (!aDirectory.exists())
|
if (!aDirectory || !aDirectory.exists())
|
||||||
return;
|
return;
|
||||||
if (!aDirectory.isDirectory())
|
if (!aDirectory.isDirectory())
|
||||||
throw new Error("Location must be a directory.");
|
throw new Error("Location must be a directory.");
|
||||||
|
@ -6882,7 +6916,7 @@ DirectoryInstallLocation.prototype = {
|
||||||
/**
|
/**
|
||||||
* Gets an array of nsIFiles for add-ons installed in this location.
|
* Gets an array of nsIFiles for add-ons installed in this location.
|
||||||
*/
|
*/
|
||||||
get addonLocations() {
|
getAddonLocations: function() {
|
||||||
let locations = new Map();
|
let locations = new Map();
|
||||||
for (let id in this._IDToFileMap) {
|
for (let id in this._IDToFileMap) {
|
||||||
locations.set(id, this._IDToFileMap[id].clone());
|
locations.set(id, this._IDToFileMap[id].clone());
|
||||||
|
@ -7201,6 +7235,128 @@ Object.assign(MutableDirectoryInstallLocation.prototype, {
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
|
/**
|
||||||
|
* An object which identifies a directory install location for system add-ons.
|
||||||
|
* The location consists of a directory which contains the add-ons installed in
|
||||||
|
* the location.
|
||||||
|
*
|
||||||
|
* @param aName
|
||||||
|
* The string identifier for the install location
|
||||||
|
* @param aDirectory
|
||||||
|
* The nsIFile directory for the install location
|
||||||
|
* @param aScope
|
||||||
|
* The scope of add-ons installed in this location
|
||||||
|
* @param aResetSet
|
||||||
|
* True to throw away the current add-on set
|
||||||
|
*/
|
||||||
|
function SystemAddonInstallLocation(aName, aDirectory, aScope, aResetSet) {
|
||||||
|
this._baseDir = aDirectory;
|
||||||
|
|
||||||
|
if (aResetSet) {
|
||||||
|
this._addonSet = { schema: 1, addons: {} };
|
||||||
|
this._saveAddonSet(this._addonSet);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
this._addonSet = this._loadAddonSet();
|
||||||
|
}
|
||||||
|
|
||||||
|
this._directory = null;
|
||||||
|
if (this._addonSet.directory) {
|
||||||
|
this._directory = aDirectory.clone();
|
||||||
|
this._directory.append(this._addonSet.directory);
|
||||||
|
logger.info("SystemAddonInstallLocation scanning directory " + this._directory.path);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
logger.info("SystemAddonInstallLocation directory is missing");
|
||||||
|
}
|
||||||
|
|
||||||
|
DirectoryInstallLocation.call(this, aName, this._directory, aScope);
|
||||||
|
this.locked = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
SystemAddonInstallLocation.prototype = Object.create(DirectoryInstallLocation.prototype);
|
||||||
|
Object.assign(SystemAddonInstallLocation.prototype, {
|
||||||
|
/**
|
||||||
|
* Reads the current set of system add-ons
|
||||||
|
*/
|
||||||
|
_loadAddonSet: function() {
|
||||||
|
try {
|
||||||
|
let setStr = Preferences.get(PREF_SYSTEM_ADDON_SET, null);
|
||||||
|
if (setStr) {
|
||||||
|
let addonSet = JSON.parse(setStr);
|
||||||
|
if ((typeof addonSet == "object") && addonSet.schema == 1)
|
||||||
|
return addonSet;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (e) {
|
||||||
|
logger.error("Malformed system add-on set, resetting.");
|
||||||
|
}
|
||||||
|
|
||||||
|
return { schema: 1, addons: {} };
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Saves the current set of system add-ons
|
||||||
|
*/
|
||||||
|
_saveAddonSet: function(aAddonSet) {
|
||||||
|
Preferences.set(PREF_SYSTEM_ADDON_SET, JSON.stringify(aAddonSet));
|
||||||
|
},
|
||||||
|
|
||||||
|
getAddonLocations: function() {
|
||||||
|
let addons = DirectoryInstallLocation.prototype.getAddonLocations.call(this);
|
||||||
|
|
||||||
|
// Strip out any unexpected add-ons from the list
|
||||||
|
for (let id of addons.keys()) {
|
||||||
|
if (!(id in this._addonSet.addons))
|
||||||
|
addons.delete(id);
|
||||||
|
}
|
||||||
|
|
||||||
|
return addons;
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Tests whether updated system add-ons are expected.
|
||||||
|
*/
|
||||||
|
isActive: function() {
|
||||||
|
return this._directory != null;
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Tests whether the loaded add-on information matches what is expected.
|
||||||
|
*/
|
||||||
|
isValid: function(aAddons) {
|
||||||
|
for (let id of Object.keys(this._addonSet.addons)) {
|
||||||
|
if (!aAddons.has(id)) {
|
||||||
|
logger.warn("Expected add-on " + id + " is missing from the system add-on location.");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
let addon = aAddons.get(id);
|
||||||
|
if (addon.appDisabled) {
|
||||||
|
logger.warn("System add-on " + id + " isn't compatible with the application.");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (addon.unpack) {
|
||||||
|
logger.warn("System add-on " + id + " isn't a packed add-on.");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!addon.bootstrap) {
|
||||||
|
logger.warn("System add-on " + id + " isn't restartless.");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (addon.version != this._addonSet.addons[id].version) {
|
||||||
|
logger.warn("System add-on " + id + " wasn't the correct version.");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
#ifdef XP_WIN
|
#ifdef XP_WIN
|
||||||
/**
|
/**
|
||||||
* An object that identifies a registry install location for add-ons. The location
|
* An object that identifies a registry install location for add-ons. The location
|
||||||
|
@ -7307,7 +7463,7 @@ WinRegInstallLocation.prototype = {
|
||||||
/**
|
/**
|
||||||
* Gets an array of nsIFiles for add-ons installed in this location.
|
* Gets an array of nsIFiles for add-ons installed in this location.
|
||||||
*/
|
*/
|
||||||
get addonLocations() {
|
getAddonLocations: function() {
|
||||||
let locations = new Map();
|
let locations = new Map();
|
||||||
for (let id in this._IDToFileMap) {
|
for (let id in this._IDToFileMap) {
|
||||||
locations.set(id, this._IDToFileMap[id].clone());
|
locations.set(id, this._IDToFileMap[id].clone());
|
||||||
|
|
|
@ -1497,10 +1497,13 @@ this.XPIDatabaseReconcile = {
|
||||||
* Returns a map of ID -> add-on. When the same add-on ID exists in multiple
|
* Returns a map of ID -> add-on. When the same add-on ID exists in multiple
|
||||||
* install locations the highest priority location is chosen.
|
* install locations the highest priority location is chosen.
|
||||||
*/
|
*/
|
||||||
flattenByID(addonMap) {
|
flattenByID(addonMap, hideLocation) {
|
||||||
let map = new Map();
|
let map = new Map();
|
||||||
|
|
||||||
for (let installLocation of XPIProvider.installLocations) {
|
for (let installLocation of XPIProvider.installLocations) {
|
||||||
|
if (installLocation.name == hideLocation)
|
||||||
|
continue;
|
||||||
|
|
||||||
let locationMap = addonMap.get(installLocation.name);
|
let locationMap = addonMap.get(installLocation.name);
|
||||||
if (!locationMap)
|
if (!locationMap)
|
||||||
continue;
|
continue;
|
||||||
|
@ -1608,7 +1611,12 @@ this.XPIDatabaseReconcile = {
|
||||||
aNewAddon._installLocation = aInstallLocation;
|
aNewAddon._installLocation = aInstallLocation;
|
||||||
aNewAddon.installDate = aAddonState.mtime;
|
aNewAddon.installDate = aAddonState.mtime;
|
||||||
aNewAddon.updateDate = aAddonState.mtime;
|
aNewAddon.updateDate = aAddonState.mtime;
|
||||||
aNewAddon.foreignInstall = isDetectedInstall;
|
|
||||||
|
// Assume that add-ons in the system add-ons install location aren't
|
||||||
|
// foreign and should default to enabled.
|
||||||
|
aNewAddon.foreignInstall = isDetectedInstall &&
|
||||||
|
aInstallLocation.name != KEY_APP_SYSTEM_ADDONS &&
|
||||||
|
aInstallLocation.name != KEY_APP_SYSTEM_DEFAULTS;
|
||||||
|
|
||||||
// appDisabled depends on whether the add-on is a foreignInstall so update
|
// appDisabled depends on whether the add-on is a foreignInstall so update
|
||||||
aNewAddon.appDisabled = !isUsableAddon(aNewAddon);
|
aNewAddon.appDisabled = !isUsableAddon(aNewAddon);
|
||||||
|
@ -1886,7 +1894,8 @@ this.XPIDatabaseReconcile = {
|
||||||
// has changed
|
// has changed
|
||||||
let newAddon = loadedManifest(installLocation, id);
|
let newAddon = loadedManifest(installLocation, id);
|
||||||
if (newAddon || oldAddon.updateDate != xpiState.mtime ||
|
if (newAddon || oldAddon.updateDate != xpiState.mtime ||
|
||||||
(aUpdateCompatibility && installLocation.name == KEY_APP_GLOBAL)) {
|
(aUpdateCompatibility && (installLocation.name == KEY_APP_GLOBAL ||
|
||||||
|
installLocation.name == KEY_APP_SYSTEM_DEFAULTS))) {
|
||||||
newAddon = this.updateMetadata(installLocation, oldAddon, xpiState, newAddon);
|
newAddon = this.updateMetadata(installLocation, oldAddon, xpiState, newAddon);
|
||||||
}
|
}
|
||||||
else if (oldAddon.descriptor != xpiState.descriptor) {
|
else if (oldAddon.descriptor != xpiState.descriptor) {
|
||||||
|
@ -1943,8 +1952,24 @@ this.XPIDatabaseReconcile = {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Validate the updated system add-ons
|
||||||
|
let systemAddonLocation = XPIProvider.installLocationsByName[KEY_APP_SYSTEM_ADDONS];
|
||||||
|
let addons = currentAddons.get(KEY_APP_SYSTEM_ADDONS) || new Map();
|
||||||
|
|
||||||
|
let hideLocation;
|
||||||
|
if (systemAddonLocation.isActive() && systemAddonLocation.isValid(addons)) {
|
||||||
|
// Hide the system add-on defaults
|
||||||
|
logger.info("Hiding the default system add-ons.");
|
||||||
|
hideLocation = KEY_APP_SYSTEM_DEFAULTS;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
// Hide the system add-on updates
|
||||||
|
logger.info("Hiding the updated system add-ons.");
|
||||||
|
hideLocation = KEY_APP_SYSTEM_ADDONS;
|
||||||
|
}
|
||||||
|
|
||||||
let previousVisible = this.getVisibleAddons(previousAddons);
|
let previousVisible = this.getVisibleAddons(previousAddons);
|
||||||
let currentVisible = this.flattenByID(currentAddons);
|
let currentVisible = this.flattenByID(currentAddons, hideLocation);
|
||||||
let sawActiveTheme = false;
|
let sawActiveTheme = false;
|
||||||
XPIProvider.bootstrappedAddons = {};
|
XPIProvider.bootstrappedAddons = {};
|
||||||
|
|
||||||
|
@ -2012,11 +2037,9 @@ this.XPIDatabaseReconcile = {
|
||||||
// and still exists then call its uninstall method.
|
// and still exists then call its uninstall method.
|
||||||
if (previousAddon.bootstrap && previousAddon._installLocation &&
|
if (previousAddon.bootstrap && previousAddon._installLocation &&
|
||||||
currentAddon._installLocation != previousAddon._installLocation &&
|
currentAddon._installLocation != previousAddon._installLocation &&
|
||||||
currentAddons.get(previousAddon._installLocation.name).has(id)) {
|
previousAddon._sourceBundle.exists()) {
|
||||||
|
|
||||||
let file = Cc["@mozilla.org/file/local;1"].createInstance(Ci.nsIFile);
|
XPIProvider.callBootstrapMethod(previousAddon, previousAddon._sourceBundle,
|
||||||
file.persistentDescriptor = previousAddon._sourceBundle.persistentDescriptor;
|
|
||||||
XPIProvider.callBootstrapMethod(previousAddon, file,
|
|
||||||
"uninstall", installReason,
|
"uninstall", installReason,
|
||||||
{ newVersion: currentAddon.version });
|
{ newVersion: currentAddon.version });
|
||||||
XPIProvider.unloadBootstrapScope(previousAddon.id);
|
XPIProvider.unloadBootstrapScope(previousAddon.id);
|
||||||
|
@ -2071,6 +2094,15 @@ this.XPIDatabaseReconcile = {
|
||||||
AddonManagerPrivate.addStartupChange(AddonManager.STARTUP_CHANGE_UNINSTALLED, id);
|
AddonManagerPrivate.addStartupChange(AddonManager.STARTUP_CHANGE_UNINSTALLED, id);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Make sure add-ons from hidden locations are marked invisible and inactive
|
||||||
|
let locationAddonMap = currentAddons.get(hideLocation);
|
||||||
|
if (locationAddonMap) {
|
||||||
|
for (let addon of locationAddonMap.values()) {
|
||||||
|
addon.visible = false;
|
||||||
|
addon.active = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// None of the active add-ons match the selected theme, enable the default.
|
// None of the active add-ons match the selected theme, enable the default.
|
||||||
if (!sawActiveTheme) {
|
if (!sawActiveTheme) {
|
||||||
XPIProvider.enableDefaultTheme();
|
XPIProvider.enableDefaultTheme();
|
||||||
|
|
Двоичные данные
toolkit/mozapps/extensions/test/xpcshell/data/system_addons/app1/features/system1@tests.mozilla.org.xpi
Normal file
Двоичные данные
toolkit/mozapps/extensions/test/xpcshell/data/system_addons/app1/features/system1@tests.mozilla.org.xpi
Normal file
Двоичный файл не отображается.
Двоичные данные
toolkit/mozapps/extensions/test/xpcshell/data/system_addons/app1/features/system2@tests.mozilla.org.xpi
Normal file
Двоичные данные
toolkit/mozapps/extensions/test/xpcshell/data/system_addons/app1/features/system2@tests.mozilla.org.xpi
Normal file
Двоичный файл не отображается.
18
toolkit/mozapps/extensions/test/xpcshell/data/system_addons/app1/features/system_1_1/bootstrap.js
поставляемый
Normal file
18
toolkit/mozapps/extensions/test/xpcshell/data/system_addons/app1/features/system_1_1/bootstrap.js
поставляемый
Normal file
|
@ -0,0 +1,18 @@
|
||||||
|
Components.utils.import("resource://gre/modules/Services.jsm");
|
||||||
|
|
||||||
|
const ID = "system1@tests.mozilla.org";
|
||||||
|
const VERSION = "1.0";
|
||||||
|
|
||||||
|
function install(data, reason) {
|
||||||
|
}
|
||||||
|
|
||||||
|
function startup(data, reason) {
|
||||||
|
Services.prefs.setCharPref("bootstraptest." + ID + ".active_version", VERSION);
|
||||||
|
}
|
||||||
|
|
||||||
|
function shutdown(data, reason) {
|
||||||
|
Services.prefs.clearUserPref("bootstraptest." + ID + ".active_version");
|
||||||
|
}
|
||||||
|
|
||||||
|
function uninstall(data, reason) {
|
||||||
|
}
|
|
@ -0,0 +1,23 @@
|
||||||
|
<?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>system1@tests.mozilla.org</em:id>
|
||||||
|
<em:version>1.0</em:version>
|
||||||
|
<em:bootstrap>true</em:bootstrap>
|
||||||
|
|
||||||
|
<!-- Front End MetaData -->
|
||||||
|
<em:name>System Add-on 1</em:name>
|
||||||
|
|
||||||
|
<em:targetApplication>
|
||||||
|
<Description>
|
||||||
|
<em:id>xpcshell@tests.mozilla.org</em:id>
|
||||||
|
<em:minVersion>1</em:minVersion>
|
||||||
|
<em:maxVersion>5</em:maxVersion>
|
||||||
|
</Description>
|
||||||
|
</em:targetApplication>
|
||||||
|
|
||||||
|
</Description>
|
||||||
|
</RDF>
|
18
toolkit/mozapps/extensions/test/xpcshell/data/system_addons/app1/features/system_2_1/bootstrap.js
поставляемый
Normal file
18
toolkit/mozapps/extensions/test/xpcshell/data/system_addons/app1/features/system_2_1/bootstrap.js
поставляемый
Normal file
|
@ -0,0 +1,18 @@
|
||||||
|
Components.utils.import("resource://gre/modules/Services.jsm");
|
||||||
|
|
||||||
|
const ID = "system2@tests.mozilla.org";
|
||||||
|
const VERSION = "1.0";
|
||||||
|
|
||||||
|
function install(data, reason) {
|
||||||
|
}
|
||||||
|
|
||||||
|
function startup(data, reason) {
|
||||||
|
Services.prefs.setCharPref("bootstraptest." + ID + ".active_version", VERSION);
|
||||||
|
}
|
||||||
|
|
||||||
|
function shutdown(data, reason) {
|
||||||
|
Services.prefs.clearUserPref("bootstraptest." + ID + ".active_version");
|
||||||
|
}
|
||||||
|
|
||||||
|
function uninstall(data, reason) {
|
||||||
|
}
|
|
@ -0,0 +1,23 @@
|
||||||
|
<?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>system2@tests.mozilla.org</em:id>
|
||||||
|
<em:version>1.0</em:version>
|
||||||
|
<em:bootstrap>true</em:bootstrap>
|
||||||
|
|
||||||
|
<!-- Front End MetaData -->
|
||||||
|
<em:name>System Add-on 2</em:name>
|
||||||
|
|
||||||
|
<em:targetApplication>
|
||||||
|
<Description>
|
||||||
|
<em:id>xpcshell@tests.mozilla.org</em:id>
|
||||||
|
<em:minVersion>1</em:minVersion>
|
||||||
|
<em:maxVersion>5</em:maxVersion>
|
||||||
|
</Description>
|
||||||
|
</em:targetApplication>
|
||||||
|
|
||||||
|
</Description>
|
||||||
|
</RDF>
|
Двоичные данные
toolkit/mozapps/extensions/test/xpcshell/data/system_addons/app2/features/system1@tests.mozilla.org.xpi
Normal file
Двоичные данные
toolkit/mozapps/extensions/test/xpcshell/data/system_addons/app2/features/system1@tests.mozilla.org.xpi
Normal file
Двоичный файл не отображается.
Двоичные данные
toolkit/mozapps/extensions/test/xpcshell/data/system_addons/app2/features/system3@tests.mozilla.org.xpi
Normal file
Двоичные данные
toolkit/mozapps/extensions/test/xpcshell/data/system_addons/app2/features/system3@tests.mozilla.org.xpi
Normal file
Двоичный файл не отображается.
18
toolkit/mozapps/extensions/test/xpcshell/data/system_addons/app2/features/system_1_2/bootstrap.js
поставляемый
Normal file
18
toolkit/mozapps/extensions/test/xpcshell/data/system_addons/app2/features/system_1_2/bootstrap.js
поставляемый
Normal file
|
@ -0,0 +1,18 @@
|
||||||
|
Components.utils.import("resource://gre/modules/Services.jsm");
|
||||||
|
|
||||||
|
const ID = "system1@tests.mozilla.org";
|
||||||
|
const VERSION = "2.0";
|
||||||
|
|
||||||
|
function install(data, reason) {
|
||||||
|
}
|
||||||
|
|
||||||
|
function startup(data, reason) {
|
||||||
|
Services.prefs.setCharPref("bootstraptest." + ID + ".active_version", VERSION);
|
||||||
|
}
|
||||||
|
|
||||||
|
function shutdown(data, reason) {
|
||||||
|
Services.prefs.clearUserPref("bootstraptest." + ID + ".active_version");
|
||||||
|
}
|
||||||
|
|
||||||
|
function uninstall(data, reason) {
|
||||||
|
}
|
|
@ -0,0 +1,23 @@
|
||||||
|
<?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>system1@tests.mozilla.org</em:id>
|
||||||
|
<em:version>2.0</em:version>
|
||||||
|
<em:bootstrap>true</em:bootstrap>
|
||||||
|
|
||||||
|
<!-- Front End MetaData -->
|
||||||
|
<em:name>System Add-on 1</em:name>
|
||||||
|
|
||||||
|
<em:targetApplication>
|
||||||
|
<Description>
|
||||||
|
<em:id>xpcshell@tests.mozilla.org</em:id>
|
||||||
|
<em:minVersion>1</em:minVersion>
|
||||||
|
<em:maxVersion>5</em:maxVersion>
|
||||||
|
</Description>
|
||||||
|
</em:targetApplication>
|
||||||
|
|
||||||
|
</Description>
|
||||||
|
</RDF>
|
18
toolkit/mozapps/extensions/test/xpcshell/data/system_addons/app2/features/system_3_1/bootstrap.js
поставляемый
Normal file
18
toolkit/mozapps/extensions/test/xpcshell/data/system_addons/app2/features/system_3_1/bootstrap.js
поставляемый
Normal file
|
@ -0,0 +1,18 @@
|
||||||
|
Components.utils.import("resource://gre/modules/Services.jsm");
|
||||||
|
|
||||||
|
const ID = "system3@tests.mozilla.org";
|
||||||
|
const VERSION = "1.0";
|
||||||
|
|
||||||
|
function install(data, reason) {
|
||||||
|
}
|
||||||
|
|
||||||
|
function startup(data, reason) {
|
||||||
|
Services.prefs.setCharPref("bootstraptest." + ID + ".active_version", VERSION);
|
||||||
|
}
|
||||||
|
|
||||||
|
function shutdown(data, reason) {
|
||||||
|
Services.prefs.clearUserPref("bootstraptest." + ID + ".active_version");
|
||||||
|
}
|
||||||
|
|
||||||
|
function uninstall(data, reason) {
|
||||||
|
}
|
|
@ -0,0 +1,23 @@
|
||||||
|
<?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>system3@tests.mozilla.org</em:id>
|
||||||
|
<em:version>1.0</em:version>
|
||||||
|
<em:bootstrap>true</em:bootstrap>
|
||||||
|
|
||||||
|
<!-- Front End MetaData -->
|
||||||
|
<em:name>System Add-on 3</em:name>
|
||||||
|
|
||||||
|
<em:targetApplication>
|
||||||
|
<Description>
|
||||||
|
<em:id>xpcshell@tests.mozilla.org</em:id>
|
||||||
|
<em:minVersion>1</em:minVersion>
|
||||||
|
<em:maxVersion>5</em:maxVersion>
|
||||||
|
</Description>
|
||||||
|
</em:targetApplication>
|
||||||
|
|
||||||
|
</Description>
|
||||||
|
</RDF>
|
|
@ -1026,7 +1026,7 @@ function getFileForAddon(aDir, aId) {
|
||||||
function registerDirectory(aKey, aDir) {
|
function registerDirectory(aKey, aDir) {
|
||||||
var dirProvider = {
|
var dirProvider = {
|
||||||
getFile: function(aProp, aPersistent) {
|
getFile: function(aProp, aPersistent) {
|
||||||
aPersistent.value = true;
|
aPersistent.value = false;
|
||||||
if (aProp == aKey)
|
if (aProp == aKey)
|
||||||
return aDir.clone();
|
return aDir.clone();
|
||||||
return null;
|
return null;
|
||||||
|
|
|
@ -0,0 +1,200 @@
|
||||||
|
// Tests that we reset to the default system add-ons correctly when switching
|
||||||
|
// application versions
|
||||||
|
const PREF_SYSTEM_ADDON_SET = "extensions.systemAddonSet";
|
||||||
|
|
||||||
|
const featureDir = gProfD.clone();
|
||||||
|
featureDir.append("features");
|
||||||
|
|
||||||
|
const distroDir = do_get_file("data/system_addons/app0");
|
||||||
|
registerDirectory("XREAppDist", distroDir);
|
||||||
|
|
||||||
|
createAppInfo("xpcshell@tests.mozilla.org", "XPCShell", "0");
|
||||||
|
|
||||||
|
function makeUUID() {
|
||||||
|
let uuidGen = AM_Cc["@mozilla.org/uuid-generator;1"].
|
||||||
|
getService(AM_Ci.nsIUUIDGenerator);
|
||||||
|
return uuidGen.generateUUID().toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
function* check_installed(inProfile, ...versions) {
|
||||||
|
let expectedDir;
|
||||||
|
if (inProfile) {
|
||||||
|
expectedDir = featureDir;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
expectedDir = distroDir.clone();
|
||||||
|
expectedDir.append("features");
|
||||||
|
}
|
||||||
|
|
||||||
|
for (let i = 0; i < versions.length; i++) {
|
||||||
|
let id = "system" + (i + 1) + "@tests.mozilla.org";
|
||||||
|
let addon = yield promiseAddonByID(id);
|
||||||
|
|
||||||
|
if (versions[i]) {
|
||||||
|
// Add-on should be installed
|
||||||
|
do_check_neq(addon, null);
|
||||||
|
do_check_eq(addon.version, versions[i]);
|
||||||
|
do_check_true(addon.isActive);
|
||||||
|
do_check_false(addon.foreignInstall);
|
||||||
|
|
||||||
|
// Verify the add-ons file is in the right place
|
||||||
|
let file = expectedDir.clone();
|
||||||
|
file.append(id + ".xpi");
|
||||||
|
do_check_true(file.exists());
|
||||||
|
do_check_true(file.isFile());
|
||||||
|
|
||||||
|
let uri = addon.getResourceURI(null);
|
||||||
|
do_check_true(uri instanceof AM_Ci.nsIFileURL);
|
||||||
|
do_check_eq(uri.file.path, file.path);
|
||||||
|
|
||||||
|
// Verify the add-on actually started
|
||||||
|
let installed = Services.prefs.getCharPref("bootstraptest." + id + ".active_version");
|
||||||
|
do_check_eq(installed, versions[i]);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
// Add-on should not be installed
|
||||||
|
do_check_eq(addon, null);
|
||||||
|
|
||||||
|
try {
|
||||||
|
Services.prefs.getCharPref("bootstraptest." + id + ".active_version");
|
||||||
|
do_throw("Expected pref to be missing");
|
||||||
|
}
|
||||||
|
catch (e) {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Test with a missing features directory
|
||||||
|
add_task(function* test_missing_app_dir() {
|
||||||
|
startupManager();
|
||||||
|
|
||||||
|
yield check_installed(false, null, null, null);
|
||||||
|
|
||||||
|
do_check_false(featureDir.exists());
|
||||||
|
|
||||||
|
yield promiseShutdownManager();
|
||||||
|
});
|
||||||
|
|
||||||
|
// Add some features in a new version
|
||||||
|
add_task(function* test_new_version() {
|
||||||
|
gAppInfo.version = "1";
|
||||||
|
distroDir.leafName = "app1";
|
||||||
|
startupManager();
|
||||||
|
|
||||||
|
yield check_installed(false, "1.0", "1.0", null);
|
||||||
|
|
||||||
|
do_check_false(featureDir.exists());
|
||||||
|
|
||||||
|
yield promiseShutdownManager();
|
||||||
|
});
|
||||||
|
|
||||||
|
// Another new version swaps one feature and upgrades another
|
||||||
|
add_task(function* test_upgrade() {
|
||||||
|
gAppInfo.version = "2";
|
||||||
|
distroDir.leafName = "app2";
|
||||||
|
startupManager();
|
||||||
|
|
||||||
|
yield check_installed(false, "2.0", null, "1.0");
|
||||||
|
|
||||||
|
do_check_false(featureDir.exists());
|
||||||
|
|
||||||
|
yield promiseShutdownManager();
|
||||||
|
});
|
||||||
|
|
||||||
|
// Downgrade
|
||||||
|
add_task(function* test_downgrade() {
|
||||||
|
gAppInfo.version = "1";
|
||||||
|
distroDir.leafName = "app1";
|
||||||
|
startupManager();
|
||||||
|
|
||||||
|
yield check_installed(false, "1.0", "1.0", null);
|
||||||
|
|
||||||
|
do_check_false(featureDir.exists());
|
||||||
|
|
||||||
|
yield promiseShutdownManager();
|
||||||
|
});
|
||||||
|
|
||||||
|
// Fake a mid-cycle install
|
||||||
|
add_task(function* test_updated() {
|
||||||
|
// Create a random dir to install into
|
||||||
|
let dirname = makeUUID();
|
||||||
|
FileUtils.getDir("ProfD", ["features", dirname], true);
|
||||||
|
featureDir.append(dirname);
|
||||||
|
|
||||||
|
// Copy in the system add-ons
|
||||||
|
let file = do_get_file("data/system_addons/app1/features/system2@tests.mozilla.org.xpi");
|
||||||
|
file.copyTo(featureDir, file.leafName);
|
||||||
|
file = do_get_file("data/system_addons/app2/features/system3@tests.mozilla.org.xpi");
|
||||||
|
file.copyTo(featureDir, file.leafName);
|
||||||
|
|
||||||
|
// Inject it into the system set
|
||||||
|
let addonSet = {
|
||||||
|
schema: 1,
|
||||||
|
directory: dirname,
|
||||||
|
addons: {
|
||||||
|
"system2@tests.mozilla.org": {
|
||||||
|
version: "1.0"
|
||||||
|
},
|
||||||
|
"system3@tests.mozilla.org": {
|
||||||
|
version: "1.0"
|
||||||
|
},
|
||||||
|
}
|
||||||
|
};
|
||||||
|
Services.prefs.setCharPref(PREF_SYSTEM_ADDON_SET, JSON.stringify(addonSet));
|
||||||
|
|
||||||
|
startupManager(false);
|
||||||
|
|
||||||
|
yield check_installed(true, null, "1.0", "1.0");
|
||||||
|
|
||||||
|
yield promiseShutdownManager();
|
||||||
|
});
|
||||||
|
|
||||||
|
// An additional add-on in the directory should be ignored
|
||||||
|
add_task(function* test_skips_additional() {
|
||||||
|
// Copy in the system add-ons
|
||||||
|
let file = do_get_file("data/system_addons/app1/features/system1@tests.mozilla.org.xpi");
|
||||||
|
file.copyTo(featureDir, file.leafName);
|
||||||
|
|
||||||
|
startupManager(false);
|
||||||
|
|
||||||
|
yield check_installed(true, null, "1.0", "1.0");
|
||||||
|
|
||||||
|
yield promiseShutdownManager();
|
||||||
|
});
|
||||||
|
|
||||||
|
// Missing add-on should revert to the default set
|
||||||
|
add_task(function* test_revert() {
|
||||||
|
manuallyUninstall(featureDir, "system2@tests.mozilla.org");
|
||||||
|
|
||||||
|
startupManager(false);
|
||||||
|
|
||||||
|
// With system add-on 2 gone the updated set is now invalid so it reverts to
|
||||||
|
// the default set which is system add-ons 1 and 2.
|
||||||
|
yield check_installed(false, "1.0", "1.0", null);
|
||||||
|
|
||||||
|
yield promiseShutdownManager();
|
||||||
|
});
|
||||||
|
|
||||||
|
// Putting it back will make the set work again
|
||||||
|
add_task(function* test_reuse() {
|
||||||
|
let file = do_get_file("data/system_addons/app1/features/system2@tests.mozilla.org.xpi");
|
||||||
|
file.copyTo(featureDir, file.leafName);
|
||||||
|
|
||||||
|
startupManager(false);
|
||||||
|
|
||||||
|
yield check_installed(true, null, "1.0", "1.0");
|
||||||
|
|
||||||
|
yield promiseShutdownManager();
|
||||||
|
});
|
||||||
|
|
||||||
|
// Making the pref corrupt should revert to the default set
|
||||||
|
add_task(function* test_corrupt_pref() {
|
||||||
|
Services.prefs.setCharPref(PREF_SYSTEM_ADDON_SET, "foo");
|
||||||
|
|
||||||
|
startupManager(false);
|
||||||
|
|
||||||
|
yield check_installed(false, "1.0", "1.0", null);
|
||||||
|
|
||||||
|
yield promiseShutdownManager();
|
||||||
|
});
|
|
@ -24,6 +24,7 @@ skip-if = appname != "firefox"
|
||||||
[test_provider_unsafe_access_shutdown.js]
|
[test_provider_unsafe_access_shutdown.js]
|
||||||
[test_provider_unsafe_access_startup.js]
|
[test_provider_unsafe_access_startup.js]
|
||||||
[test_shutdown.js]
|
[test_shutdown.js]
|
||||||
|
[test_system_reset.js]
|
||||||
[test_XPIcancel.js]
|
[test_XPIcancel.js]
|
||||||
[test_XPIStates.js]
|
[test_XPIStates.js]
|
||||||
|
|
||||||
|
|
Загрузка…
Ссылка в новой задаче