Bug 915838 - Provide add-ons a standard directory to store data, settings. r=Unfocused

This commit is contained in:
Alexander J. Vincent 2013-10-16 17:10:50 -07:00
parent 2babd2681f
commit 31f85dde6c
4 changed files with 147 additions and 8 deletions

View File

@ -397,7 +397,7 @@ SafeInstallOperation.prototype = {
* The directory to move into, this is expected to be an empty
* directory.
*/
move: function SIO_move(aFile, aTargetDirectory) {
moveUnder: function SIO_move(aFile, aTargetDirectory) {
try {
this._installDirEntry(aFile, aTargetDirectory, false);
}
@ -407,6 +407,27 @@ SafeInstallOperation.prototype = {
}
},
/**
* Renames a file to a new location. If an error occurs then all
* files that have been moved will be moved back to their original location.
*
* @param aOldLocation
* The old location of the file.
* @param aNewLocation
* The new location of the file.
*/
moveTo: function(aOldLocation, aNewLocation) {
try {
let oldFile = aOldLocation.clone(), newFile = aNewLocation.clone();
oldFile.moveTo(newFile.parent, newFile.leafName);
this._installedFiles.push({ oldFile: oldFile, newFile: newFile, isMoveTo: true});
}
catch(e) {
this.rollback();
throw e;
}
},
/**
* Copies a file or directory into a new directory. If an error occurs then
* all new files that have been created will be removed.
@ -435,7 +456,10 @@ SafeInstallOperation.prototype = {
rollback: function SIO_rollback() {
while (this._installedFiles.length > 0) {
let move = this._installedFiles.pop();
if (move.newFile.isDirectory()) {
if (move.isMoveTo) {
move.newFile.moveTo(oldDir.parent, oldDir.leafName);
}
else if (move.newFile.isDirectory()) {
let oldDir = move.oldFile.parent.clone();
oldDir.append(move.oldFile.leafName);
oldDir.create(Ci.nsIFile.DIRECTORY_TYPE, FileUtils.PERMS_DIRECTORY);
@ -6149,6 +6173,22 @@ AddonInternal.prototype = {
this.appDisabled = !isUsableAddon(this);
},
/**
* getDataDirectory tries to execute the callback with two arguments:
* 1) the path of the data directory within the profile,
* 2) any exception generated from trying to build it.
*/
getDataDirectory: function(callback) {
let parentPath = OS.Path.join(OS.Constants.Path.profileDir, "extension-data");
let dirPath = OS.Path.join(parentPath, this.id);
Task.spawn(function*() {
yield OS.File.makeDir(parentPath, {ignoreExisting: true});
yield OS.File.makeDir(dirPath, {ignoreExisting: true});
}).then(() => callback(dirPath, null),
e => callback(dirPath, e));
},
/**
* toJSON is called by JSON.stringify in order to create a filtered version
* of this object to be serialized to a JSON file. A new object is returned
@ -6251,7 +6291,8 @@ function AddonWrapper(aAddon) {
["id", "syncGUID", "version", "type", "isCompatible", "isPlatformCompatible",
"providesUpdatesSecurely", "blocklistState", "blocklistURL", "appDisabled",
"softDisabled", "skinnable", "size", "foreignInstall", "hasBinaryComponents",
"strictCompatibility", "compatibilityOverrides", "updateURL"].forEach(function(aProp) {
"strictCompatibility", "compatibilityOverrides", "updateURL",
"getDataDirectory"].forEach(function(aProp) {
this.__defineGetter__(aProp, function AddonWrapper_propertyGetter() aAddon[aProp]);
}, this);
@ -7052,13 +7093,13 @@ DirectoryInstallLocation.prototype = {
file.append(aId);
if (file.exists())
transaction.move(file, trashDir);
transaction.moveUnder(file, trashDir);
file = self._directory.clone();
file.append(aId + ".xpi");
if (file.exists()) {
flushJarCache(file);
transaction.move(file, trashDir);
transaction.moveUnder(file, trashDir);
}
}
@ -7066,9 +7107,34 @@ DirectoryInstallLocation.prototype = {
// temporary directory
try {
moveOldAddon(aId);
if (aExistingAddonID && aExistingAddonID != aId)
if (aExistingAddonID && aExistingAddonID != aId) {
moveOldAddon(aExistingAddonID);
{
// Move the data directories.
/* XXX ajvincent We can't use OS.File: installAddon isn't compatible
* with Promises, nor is SafeInstallOperation. Bug 945540 has been filed
* for porting to OS.File.
*/
let oldDataDir = FileUtils.getDir(
KEY_PROFILEDIR, ["extension-data", aExistingAddonID], false, true
);
if (oldDataDir.exists()) {
let newDataDir = FileUtils.getDir(
KEY_PROFILEDIR, ["extension-data", aId], false, true
);
if (newDataDir.exists()) {
let trashData = trashDir.clone();
trashData.append("data-directory");
transaction.moveUnder(newDataDir, trashData);
}
transaction.moveTo(oldDataDir, newDataDir);
}
}
}
if (aCopy) {
transaction.copy(aSource, this._directory);
}
@ -7076,7 +7142,7 @@ DirectoryInstallLocation.prototype = {
if (aSource.isFile())
flushJarCache(aSource);
transaction.move(aSource, this._directory);
transaction.moveUnder(aSource, this._directory);
}
}
finally {
@ -7149,7 +7215,7 @@ DirectoryInstallLocation.prototype = {
let transaction = new SafeInstallOperation();
try {
transaction.move(file, trashDir);
transaction.moveUnder(file, trashDir);
}
finally {
// It isn't ideal if this cleanup fails, but it is probably better than

View File

@ -0,0 +1,22 @@
<?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>datadirectory1@tests.mozilla.org</em:id>
<em:version>1.0</em:version>
<!-- Front End MetaData -->
<em:name>Test Data Directory 1</em:name>
<em:description>Test Description</em:description>
<em:targetApplication>
<Description>
<em:id>xpcshell@tests.mozilla.org</em:id>
<em:minVersion>1</em:minVersion>
<em:maxVersion>1</em:maxVersion>
</Description>
</em:targetApplication>
</Description>
</RDF>

View File

@ -0,0 +1,50 @@
/* 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/.
*/
// Disables security checking our updates which haven't been signed
Services.prefs.setBoolPref("extensions.checkUpdateSecurity", false);
var ADDON = {
id: "datadirectory1@tests.mozilla.org",
addon: "test_data_directory"
};
var expectedDir = gProfD.clone();
expectedDir.append("extension-data");
expectedDir.append(ADDON.id);
function run_test() {
do_test_pending();
do_check_false(expectedDir.exists());
createAppInfo("xpcshell@tests.mozilla.org", "XPCShell", "2", "1.9");
startupManager();
installAllFiles([do_get_addon(ADDON.addon)], function() {
restartManager();
AddonManager.getAddonByID(ADDON.id, function(item) {
item.getDataDirectory(promise_callback);
});
});
}
function promise_callback() {
do_check_eq(arguments.length, 2);
var expectedDir = gProfD.clone();
expectedDir.append("extension-data");
expectedDir.append(ADDON.id);
do_check_eq(arguments[0], expectedDir.path);
do_check_true(expectedDir.exists());
do_check_true(expectedDir.isDirectory());
do_check_eq(arguments[1], null);
// Cleanup.
expectedDir.parent.remove(true);
do_test_finished();
}

View File

@ -151,6 +151,7 @@ fail-if = os == "android"
[test_corrupt.js]
[test_corrupt_strictcompat.js]
[test_corruptfile.js]
[test_dataDirectory.js]
[test_default_providers_pref.js]
[test_dictionary.js]
[test_langpack.js]