diff --git a/toolkit/mozapps/extensions/XPIProvider.jsm b/toolkit/mozapps/extensions/XPIProvider.jsm index 7981ad6f780..cd8224c2b5c 100644 --- a/toolkit/mozapps/extensions/XPIProvider.jsm +++ b/toolkit/mozapps/extensions/XPIProvider.jsm @@ -4334,7 +4334,8 @@ AddonInstall.prototype = { cancel: function AI_cancel() { switch (this.state) { case AddonManager.STATE_DOWNLOADING: - this.channel.cancel(Cr.NS_BINDING_ABORTED); + if (this.channel) + this.channel.cancel(Cr.NS_BINDING_ABORTED); case AddonManager.STATE_AVAILABLE: case AddonManager.STATE_DOWNLOADED: LOG("Cancelling download of " + this.sourceURI.spec); @@ -4636,6 +4637,10 @@ AddonInstall.prototype = { return; } + // If a listener changed our state then do not proceed with the download + if (this.state != AddonManager.STATE_DOWNLOADING) + return; + try { this.file = getTemporaryFile(); this.ownsTempFile = true; @@ -4872,6 +4877,10 @@ AddonInstall.prototype = { if (AddonManagerPrivate.callInstallListeners("onDownloadEnded", self.listeners, self.wrapper)) { + // If a listener changed our state then do not proceed with the install + if (self.state != AddonManager.STATE_DOWNLOADED) + return; + self.install(); if (self.linkedInstalls) { diff --git a/toolkit/mozapps/extensions/test/xpcshell/test_install.js b/toolkit/mozapps/extensions/test/xpcshell/test_install.js index 9b0a450b22c..8739bbbf8c4 100644 --- a/toolkit/mozapps/extensions/test/xpcshell/test_install.js +++ b/toolkit/mozapps/extensions/test/xpcshell/test_install.js @@ -947,7 +947,93 @@ function check_test_13(install) { AddonManager.getAddonByID("addon2@tests.mozilla.org", function(a2) { do_check_eq(a2.version, "2.0"); - end_test(); + run_test_14(); }); }); } + +// Check that cancelling the install from onDownloadStarted actually cancels it +function run_test_14() { + prepare_test({ }, [ + "onNewInstall" + ]); + + let url = "http://localhost:4444/addons/test_install2_1.xpi"; + AddonManager.getInstallForURL(url, function(install) { + ensure_test_completed(); + + do_check_eq(install.file, null); + + prepare_test({ }, [ + "onDownloadStarted" + ], check_test_14); + install.install(); + }, "application/x-xpinstall"); +} + +function check_test_14(install) { + prepare_test({ }, [ + "onDownloadCancelled" + ]); + + install.cancel(); + + ensure_test_completed(); + + install.addListener({ + onDownloadProgress: function() { + do_throw("Download should not have continued"); + }, + onDownloadEnded: function() { + do_throw("Download should not have continued"); + } + }); + + // Allow the listener to return to see if it continues downloading. The + // The listener only really tests if we give it time to see progress, the + // file check isn't ideal either + do_execute_soon(function() { + do_check_eq(install.file, null); + + run_test_15(); + }); +} + +// Checks that cancelling the install from onDownloadEnded actually cancels it +function run_test_15() { + prepare_test({ }, [ + "onNewInstall" + ]); + + let url = "http://localhost:4444/addons/test_install2_1.xpi"; + AddonManager.getInstallForURL(url, function(install) { + ensure_test_completed(); + + do_check_eq(install.file, null); + + prepare_test({ }, [ + "onDownloadStarted", + "onDownloadEnded" + ], check_test_15); + install.install(); + }, "application/x-xpinstall"); +} + +function check_test_15(install) { + prepare_test({ }, [ + "onDownloadCancelled" + ]); + + install.cancel(); + + ensure_test_completed(); + + install.addListener({ + onInstallStarted: function() { + do_throw("Install should not have continued"); + } + }); + + // Allow the listener to return to see if it starts installing + do_execute_soon(end_test); +}