Bug 570200: Errors aren't passed sanely from getInstallForFile. r=robstrong

--HG--
rename : toolkit/mozapps/extensions/test/xpinstall/corrupt.xpi => toolkit/mozapps/extensions/test/xpcshell/data/corrupt.xpi
rename : toolkit/mozapps/extensions/test/xpinstall/empty.xpi => toolkit/mozapps/extensions/test/xpcshell/data/empty.xpi
rename : toolkit/mozapps/extensions/test/xpinstall/unsigned.xpi => toolkit/mozapps/extensions/test/xpcshell/data/unsigned.xpi
This commit is contained in:
Dave Townsend 2010-06-10 16:39:47 -07:00
parent 327f96d641
commit 2ccabda62f
24 changed files with 218 additions and 54 deletions

View File

@ -809,6 +809,8 @@ var AddonManager = {
ERROR_INCORRECT_HASH: -2,
// The downloaded file seems to be corrupted in some way.
ERROR_CORRUPT_FILE: -3,
// An error occured trying to write to the filesystem.
ERROR_FILE_ACCESS: -4,
// These must be kept in sync with AddonUpdateChecker.
// No error was encountered.

View File

@ -360,6 +360,8 @@ function loadManifestFromRDF(aUri, aStream) {
});
if (!addon.id || !addon.version)
throw new Error("No ID or version in install manifest");
if (!gIDTest.test(addon.id))
throw new Error("Illegal add-on ID " + addon.id);
if (!addon.type) {
addon.type = addon.internalName ? "theme" : "extension";
@ -3554,12 +3556,23 @@ function AddonInstall(aCallback, aInstallLocation, aUrl, aHash, aName, aType,
this.wrapper = new AddonInstallWrapper(this);
this.installLocation = aInstallLocation;
this.sourceURL = aUrl;
this.hash = aHash;
this.loadGroup = aLoadGroup;
this.listeners = [];
this.existingAddon = aExistingAddon;
this.error = 0;
if (aUrl instanceof Ci.nsIFileURL) {
this.file = aUrl.file.QueryInterface(Ci.nsILocalFile);
if (!this.file.exists()) {
WARN("XPI file " + this.file.path + " does not exist");
this.state = AddonManager.STATE_DOWNLOAD_FAILED;
this.error = AddonManager.ERROR_NETWORK_FAILURE;
aCallback(this);
return;
}
this.state = AddonManager.STATE_DOWNLOADED;
this.progress = this.file.fileSize;
this.maxProgress = this.file.fileSize;
@ -3572,11 +3585,25 @@ function AddonInstall(aCallback, aInstallLocation, aUrl, aHash, aName, aType,
fis.init(this.file, -1, -1, false);
crypto.updateFromStream(fis, this.file.fileSize);
let hash = crypto.finish(true);
if (hash != this.hash)
throw new Error("Hash mismatch");
if (hash != this.hash) {
WARN("Hash mismatch");
this.state = AddonManager.STATE_DOWNLOAD_FAILED;
this.error = AddonManager.ERROR_INCORRECT_HASH;
aCallback(this);
return;
}
}
this.loadManifest();
try {
this.loadManifest();
}
catch (e) {
WARN("Invalid XPI: " + e);
this.state = AddonManager.STATE_DOWNLOAD_FAILED;
this.error = AddonManager.ERROR_CORRUPT_FILE;
aCallback(this);
return;
}
let self = this;
XPIDatabase.getVisibleAddonForID(this.addon.id, function(aAddon) {
@ -3614,7 +3641,6 @@ function AddonInstall(aCallback, aInstallLocation, aUrl, aHash, aName, aType,
this.iconURL = aIconURL;
this.progress = 0;
this.maxProgress = -1;
this.hash = aHash;
XPIProvider.installs.push(this);
AddonManagerPrivate.callInstallListeners("onNewInstall", this.listeners,
@ -3647,6 +3673,7 @@ AddonInstall.prototype = {
addon: null,
state: null,
error: null,
progress: null,
maxProgress: null,
@ -3841,10 +3868,10 @@ AddonInstall.prototype = {
catch (e) {
WARN("Unknown hash algorithm " + alg);
this.state = AddonManager.STATE_DOWNLOAD_FAILED;
this.error = AddonManager.ERROR_INCORRECT_HASH;
XPIProvider.removeActiveInstall(this);
AddonManagerPrivate.callInstallListeners("onDownloadFailed",
this.listeners, this.wrapper,
AddonManager.ERROR_INCORRECT_HASH);
this.listeners, this.wrapper);
return;
}
}
@ -3863,29 +3890,40 @@ AddonInstall.prototype = {
createInstance(Ci.nsIFileOutputStream);
this.stream.init(this.file, FileUtils.MODE_WRONLY | FileUtils.MODE_CREATE |
FileUtils.MODE_TRUNCATE, FileUtils.PERMS_FILE, 0);
}
catch (e) {
WARN("Failed to start download: " + e);
this.state = AddonManager.STATE_DOWNLOAD_FAILED;
this.error = AddonManager.ERROR_FILE_ACCESS;
XPIProvider.removeActiveInstall(this);
AddonManagerPrivate.callInstallListeners("onDownloadFailed",
this.listeners, this.wrapper);
return;
}
let listener = Cc["@mozilla.org/network/stream-listener-tee;1"].
createInstance(Ci.nsIStreamListenerTee);
listener.init(this, this.stream);
let listener = Cc["@mozilla.org/network/stream-listener-tee;1"].
createInstance(Ci.nsIStreamListenerTee);
listener.init(this, this.stream);
try {
this.channel = NetUtil.newChannel(this.sourceURL);
if (this.loadGroup)
this.channel.loadGroup = this.loadGroup;
Services.obs.addObserver(this, "network:offline-about-to-go-offline", false);
// Verify that we don't end up on an insecure channel if we haven't got a
// hash to verify with (see bug 537761 for discussion)
if (!this.hash)
this.channel.notificationCallbacks = new BadCertHandler();
this.channel.asyncOpen(listener, null);
Services.obs.addObserver(this, "network:offline-about-to-go-offline", false);
}
catch (e) {
WARN("Failed to start download: " + e);
this.state = AddonManager.STATE_DOWNLOAD_FAILED;
this.error = AddonManager.ERROR_NETWORK_FAILURE;
XPIProvider.removeActiveInstall(this);
AddonManagerPrivate.callInstallListeners("onDownloadFailed",
this.listeners, this.wrapper,
AddonManager.ERROR_NETWORK_FAILURE);
this.listeners, this.wrapper);
}
},
@ -4015,9 +4053,10 @@ AddonInstall.prototype = {
downloadFailed: function(aReason, aError) {
WARN("Download failed: " + aError + "\n");
this.state = AddonManager.STATE_DOWNLOAD_FAILED;
this.error = aReason;
XPIProvider.removeActiveInstall(this);
AddonManagerPrivate.callInstallListeners("onDownloadFailed", this.listeners,
this.wrapper, aReason);
this.wrapper);
try {
this.file.remove(true);
}
@ -4205,10 +4244,11 @@ AddonInstall.prototype = {
if (stagedAddon.exists())
stagedAddon.remove(true);
this.state = AddonManager.STATE_INSTALL_FAILED;
this.error = AddonManager.ERROR_FILE_ACCESS;
XPIProvider.removeActiveInstall(this);
AddonManagerPrivate.callInstallListeners("onInstallFailed",
this.listeners,
this.wrapper, e);
this.wrapper);
}
finally {
// If the file was downloaded then delete it
@ -4299,8 +4339,8 @@ AddonInstall.createUpdate = function(aCallback, aAddon, aUpdate) {
* The AddonInstall to create a wrapper for
*/
function AddonInstallWrapper(aInstall) {
["name", "type", "version", "iconURL", "infoURL", "file", "state", "progress",
"maxProgress", "certificate", "certName"].forEach(function(aProp) {
["name", "type", "version", "iconURL", "infoURL", "file", "state", "error",
"progress", "maxProgress", "certificate", "certName"].forEach(function(aProp) {
this.__defineGetter__(aProp, function() aInstall[aProp]);
}, this);

View File

@ -135,14 +135,14 @@ amManager.prototype = {
aCallback.onInstallEnded(uri, USER_CANCELLED);
},
onDownloadFailed: function(aInstall, aError) {
if (aError == AddonManager.ERROR_CORRUPT_FILE)
onDownloadFailed: function(aInstall) {
if (aInstall.error == AddonManager.ERROR_CORRUPT_FILE)
aCallback.onInstallEnded(uri, CANT_READ_ARCHIVE);
else
aCallback.onInstallEnded(uri, DOWNLOAD_ERROR);
},
onInstallFailed: function(aInstall, aError) {
onInstallFailed: function(aInstall) {
aCallback.onInstallEnded(uri, EXECUTION_ERROR);
},

View File

@ -89,6 +89,8 @@ function Installer(aWindow, aUrl, aInstalls) {
// Might already be a local file
if (aInstall.state == AddonManager.STATE_DOWNLOADED)
this.onDownloadEnded(aInstall);
else if (aInstall.state == AddonManager.STATE_DOWNLOAD_FAILED)
this.onDownloadFailed(aInstall);
else
aInstall.install();
}, this);
@ -136,11 +138,12 @@ Installer.prototype = {
this.checkAllDownloaded();
},
onDownloadFailed: function(aInstall, aError) {
onDownloadFailed: function(aInstall) {
aInstall.removeListener(this);
// TODO show some better error
Services.prompt.alert(this.window, "Download Failed", "The download of " + aInstall.sourceURL + " failed: " + aError);
Services.prompt.alert(this.window, "Download Failed", "The download of " +
aInstall.sourceURL + " failed: " + aInstall.error);
this.checkAllDownloaded();
},

View File

@ -616,7 +616,6 @@
</method>
<method name="onDownloadFailed">
<parameter name="aError"/>
<body><![CDATA[
this.refreshState();
]]></body>
@ -643,7 +642,6 @@
</method>
<method name="onInstallFailed">
<parameter name="aError"/>
<body><![CDATA[
this.refreshState();
]]></body>

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>bug567173</em:id>
<em:version>1.0</em:version>
<em:targetApplication>
<Description>
<em:id>xpcshell@tests.mozilla.org</em:id>
<em:minVersion>1</em:minVersion>
<em:maxVersion>1</em:maxVersion>
</Description>
</em:targetApplication>
<!-- Front End MetaData -->
<em:name>Test Bug 567173</em:name>
</Description>
</RDF>

View File

@ -53,8 +53,8 @@ function run_install_tests(callback) {
return false;
},
onDownloadFailed: function(install, status) {
is(status, expectedStatus, message);
onDownloadFailed: function(install) {
is(install.error, expectedStatus, message);
run_next_install_test();
}
});

View File

@ -0,0 +1 @@
This is a corrupt zip file

View File

@ -497,23 +497,26 @@ const InstallListener = {
if (install.state != AddonManager.STATE_DOWNLOADED &&
install.state != AddonManager.STATE_AVAILABLE)
do_throw("Bad install state " + install.state);
do_check_eq(install.error, 0);
do_check_eq("onNewInstall", gExpectedInstalls.shift());
return check_test_completed(arguments);
},
onDownloadStarted: function(install) {
do_check_eq(install.state, AddonManager.STATE_DOWNLOADING);
do_check_eq(install.error, 0);
do_check_eq("onDownloadStarted", gExpectedInstalls.shift());
return check_test_completed(arguments);
},
onDownloadEnded: function(install) {
do_check_eq(install.state, AddonManager.STATE_DOWNLOADED);
do_check_eq(install.error, 0);
do_check_eq("onDownloadEnded", gExpectedInstalls.shift());
return check_test_completed(arguments);
},
onDownloadFailed: function(install, status) {
onDownloadFailed: function(install) {
do_check_eq(install.state, AddonManager.STATE_DOWNLOAD_FAILED);
do_check_eq("onDownloadFailed", gExpectedInstalls.shift());
return check_test_completed(arguments);
@ -521,23 +524,26 @@ const InstallListener = {
onDownloadCancelled: function(install) {
do_check_eq(install.state, AddonManager.STATE_CANCELLED);
do_check_eq(install.error, 0);
do_check_eq("onDownloadCancelled", gExpectedInstalls.shift());
return check_test_completed(arguments);
},
onInstallStarted: function(install) {
do_check_eq(install.state, AddonManager.STATE_INSTALLING);
do_check_eq(install.error, 0);
do_check_eq("onInstallStarted", gExpectedInstalls.shift());
return check_test_completed(arguments);
},
onInstallEnded: function(install, newAddon) {
do_check_eq(install.state, AddonManager.STATE_INSTALLED);
do_check_eq(install.error, 0);
do_check_eq("onInstallEnded", gExpectedInstalls.shift());
return check_test_completed(arguments);
},
onInstallFailed: function(install, status) {
onInstallFailed: function(install) {
do_check_eq(install.state, AddonManager.STATE_INSTALL_FAILED);
do_check_eq("onInstallFailed", gExpectedInstalls.shift());
return check_test_completed(arguments);
@ -545,6 +551,7 @@ const InstallListener = {
onInstallCancelled: function(install) {
do_check_eq(install.state, AddonManager.STATE_CANCELLED);
do_check_eq(install.error, 0);
do_check_eq("onInstallCancelled", gExpectedInstalls.shift());
return check_test_completed(arguments);
},
@ -655,6 +662,7 @@ function installAllFiles(aFiles, aCallback, aIgnoreIncompatible) {
AddonManager.getInstallForFile(aFile, function(aInstall) {
if (!aInstall)
do_throw("No AddonInstall created for " + aFile.path);
do_check_eq(aInstall.state, AddonManager.STATE_DOWNLOADED);
if (!aIgnoreIncompatible || !aInstall.addon.appDisabled)
installs.push(aInstall);

View File

@ -0,0 +1,90 @@
/* Any copyright is dedicated to the Public Domain.
* http://creativecommons.org/publicdomain/zero/1.0/
*/
// Tests that various error conditions are handled correctly
Components.utils.import("resource://gre/modules/Services.jsm");
const profileDir = gProfD.clone();
profileDir.append("extensions");
function run_test() {
createAppInfo("xpcshell@tests.mozilla.org", "XPCShell", "1", "1.9.2");
startupManager(1);
do_test_pending();
run_test_1();
}
// Checks that a local file validates ok
function run_test_1() {
AddonManager.getInstallForFile(do_get_file("data/unsigned.xpi"), function(install) {
do_check_neq(install, null);
do_check_eq(install.state, AddonManager.STATE_DOWNLOADED);
do_check_eq(install.error, 0);
install.cancel();
run_test_2();
});
}
// Checks that a corrupt file shows an error
function run_test_2() {
AddonManager.getInstallForFile(do_get_file("data/corrupt.xpi"), function(install) {
do_check_neq(install, null);
do_check_eq(install.state, AddonManager.STATE_DOWNLOAD_FAILED);
do_check_eq(install.error, AddonManager.ERROR_CORRUPT_FILE);
run_test_3();
});
}
// Checks that an empty file shows an error
function run_test_3() {
AddonManager.getInstallForFile(do_get_file("data/empty.xpi"), function(install) {
do_check_neq(install, null);
do_check_eq(install.state, AddonManager.STATE_DOWNLOAD_FAILED);
do_check_eq(install.error, AddonManager.ERROR_CORRUPT_FILE);
run_test_4();
});
}
// Checks that a file that doesn't match its hash shows an error
function run_test_4() {
let url = Services.io.newFileURI(do_get_file("data/unsigned.xpi")).spec;
AddonManager.getInstallForURL(url, function(install) {
do_check_neq(install, null);
do_check_eq(install.state, AddonManager.STATE_DOWNLOAD_FAILED);
do_check_eq(install.error, AddonManager.ERROR_INCORRECT_HASH);
run_test_5();
}, "application/x-xpinstall", "sha1:foo");
}
// Checks that a file that doesn't exist shows an error
function run_test_4() {
let file = do_get_file("data");
file.append("missing.xpi");
AddonManager.getInstallForFile(file, function(install) {
do_check_neq(install, null);
do_check_eq(install.state, AddonManager.STATE_DOWNLOAD_FAILED);
do_check_eq(install.error, AddonManager.ERROR_NETWORK_FAILURE);
run_test_5();
});
}
// Checks that an add-on with an illegal ID shows an error
function run_test_5() {
AddonManager.getInstallForFile(do_get_addon("test_bug567173"), function(install) {
do_check_neq(install, null);
do_check_eq(install.state, AddonManager.STATE_DOWNLOAD_FAILED);
do_check_eq(install.error, AddonManager.ERROR_CORRUPT_FILE);
do_test_finished();
});
}

View File

@ -22,7 +22,7 @@ function get_auth_info() {
return [ "testuser", "testpass" ];
}
function download_failed(install, status) {
function download_failed(install) {
ok(false, "Install should not have failed");
}

View File

@ -23,8 +23,8 @@ function get_auth_info() {
return [ "baduser", "badpass" ];
}
function download_failed(install, status) {
is(status, AddonManager.ERROR_NETWORK_FAILURE, "Install should have failed");
function download_failed(install) {
is(install.error, AddonManager.ERROR_NETWORK_FAILURE, "Install should have failed");
}
function install_ended(install, addon) {

View File

@ -23,8 +23,8 @@ function get_auth_info() {
return null;
}
function download_failed(install, status) {
is(status, AddonManager.ERROR_NETWORK_FAILURE, "Install should have failed");
function download_failed(install) {
is(install.error, AddonManager.ERROR_NETWORK_FAILURE, "Install should have failed");
}
function install_ended(install, addon) {

View File

@ -20,8 +20,8 @@ function test() {
gBrowser.loadURI(TESTROOT + "installtrigger.html?" + triggers);
}
function download_failed(install, status) {
is(status, AddonManager.ERROR_INCORRECT_HASH, "Install should fail");
function download_failed(install) {
is(install.error, AddonManager.ERROR_INCORRECT_HASH, "Install should fail");
}
function finish_test(count) {

View File

@ -20,8 +20,8 @@ function test() {
gBrowser.loadURI(TESTROOT + "installtrigger.html?" + triggers);
}
function download_failed(install, status) {
is(status, AddonManager.ERROR_INCORRECT_HASH, "Install should fail");
function download_failed(install) {
is(install.error, AddonManager.ERROR_INCORRECT_HASH, "Install should fail");
}
function finish_test(count) {

View File

@ -17,8 +17,8 @@ function test() {
gBrowser.loadURI(TESTROOT + "installtrigger.html?" + triggers);
}
function download_failed(install, status) {
is(status, AddonManager.ERROR_NETWORK_FAILURE, "Install should fail");
function download_failed(install) {
is(install.error, AddonManager.ERROR_NETWORK_FAILURE, "Install should fail");
}
function finish_test(count) {

View File

@ -25,8 +25,8 @@ function test() {
gBrowser.loadURI(TESTROOT + "installtrigger.html?" + triggers);
}
function download_failed(install, status) {
is(status, AddonManager.ERROR_NETWORK_FAILURE, "Install should fail");
function download_failed(install) {
is(install.error, AddonManager.ERROR_NETWORK_FAILURE, "Install should fail");
}
function finish_test(count) {

View File

@ -15,8 +15,8 @@ function test() {
gBrowser.loadURI(TESTROOT + "installtrigger.html?" + triggers);
}
function download_failed(install, status) {
is(status, AddonManager.ERROR_CORRUPT_FILE, "Install should fail");
function download_failed(install) {
is(install.error, AddonManager.ERROR_CORRUPT_FILE, "Install should fail");
}
function finish_test(count) {

View File

@ -15,8 +15,8 @@ function test() {
gBrowser.loadURI(TESTROOT + "installtrigger.html?" + triggers);
}
function download_failed(install, status) {
is(status, AddonManager.ERROR_CORRUPT_FILE, "Install should fail");
function download_failed(install) {
is(install.error, AddonManager.ERROR_CORRUPT_FILE, "Install should fail");
}
function finish_test(count) {

View File

@ -20,8 +20,8 @@ function confirm_install(window) {
ok(false, "Should not offer to install");
}
function download_failed(install, status) {
is(status, AddonManager.ERROR_CORRUPT_FILE, "Install should fail");
function download_failed(install) {
is(install.error, AddonManager.ERROR_CORRUPT_FILE, "Install should fail");
}
function finish_test(count) {

View File

@ -216,9 +216,9 @@ var Harness = {
this.checkTestEnded();
},
onDownloadFailed: function(install, status) {
onDownloadFailed: function(install) {
if (this.downloadFailedCallback)
this.downloadFailedCallback(install, status);
this.downloadFailedCallback(install);
this.checkTestEnded();
},
@ -234,9 +234,9 @@ var Harness = {
this.checkTestEnded();
},
onInstallFailed: function(install, status) {
onInstallFailed: function(install) {
if (this.installFailedCallback)
this.installFailedCallback(install, status);
this.installFailedCallback(install);
this.checkTestEnded();
},

View File

@ -289,9 +289,9 @@ var PluginInstallService = {
gPluginInstaller.pluginInstallationProgress(pid, DOWNLOAD_FINISHED, null);
},
onDownloadFailed: function(install, error) {
onDownloadFailed: function(install) {
var pid = this.getPidForInstall(install);
switch (error) {
switch (install.error) {
case AddonManager.ERROR_NETWORK_FAILURE:
var errorMsg = getLocalizedError("error-228");
break;
@ -321,7 +321,7 @@ var PluginInstallService = {
this._fireFinishedNotification();
},
onInstallFailed: function(install, error) {
onInstallFailed: function(install) {
var pid = this.getPidForInstall(install);
gPluginInstaller.pluginInstallationProgress(pid, INSTALL_FINISHED,
getLocalizedError("error-203"));