diff --git a/common/modules/downloads.jsm b/common/modules/downloads.jsm
index 73d69f26..1bd6f65a 100644
--- a/common/modules/downloads.jsm
+++ b/common/modules/downloads.jsm
@@ -7,7 +7,7 @@
Components.utils.import("resource://dactyl/bootstrap.jsm");
defineModule("downloads", {
exports: ["Download", "Downloads", "downloads"],
- use: ["io", "services", "template", "util"]
+ use: ["io", "prefs", "services", "template", "util"]
}, this);
Cu.import("resource://gre/modules/DownloadUtils.jsm", this);
@@ -19,17 +19,19 @@ var states = iter([v, k.slice(prefix.length).toLowerCase()]
.toObject();
var Download = Class("Download", {
- init: function init(id, document) {
+ init: function init(id, list) {
let self = XPCSafeJSObjectWrapper(services.downloadManager.getDownload(id));
self.__proto__ = this;
this.instance = this;
+ this.list = list;
this.nodes = {};
util.xmlToDom(
- {self.displayName}
+ {self.displayName}
{self.targetFile.path}
@@ -50,7 +52,7 @@ var Download = Class("Download", {
{self.source.spec}
,
- document, this.nodes);
+ this.list.document, this.nodes);
for (let [key, node] in Iterator(this.nodes)) {
node.dactylDownload = self;
@@ -81,6 +83,7 @@ var Download = Class("Download", {
allowed: Class.memoize(function () let (self = this) ({
get cancel() self.cancelable && self.inState(["downloading", "paused", "starting"]),
get delete() !this.cancel && self.targetFile.exists(),
+ get launch() self.targetFile.exists() && self.inState(["finished"]),
get pause() self.inState(["downloading"]),
get remove() self.inState(["blocked_parental", "blocked_policy",
"canceled", "dirty", "failed", "finished"]),
@@ -102,6 +105,31 @@ var Download = Class("Download", {
delete: function delete() {
this.targetFile.remove(false);
this.updateStatus();
+ },
+ launch: function launch() {
+ let self = this;
+ // Behavior mimics that of the builtin Download Manager.
+ function action() {
+ try {
+ if (this.MIMEInfo && this.MIMEInfo.preferredAction == this.MIMEInfo.useHelperApp)
+ this.MIMEInfo.launchWithFile(file)
+ else
+ file.launch();
+ }
+ catch (e) {
+ services.externalProtocol.loadUrl(this.target);
+ }
+ }
+
+ let file = io.File(this.targetFile);
+ if (file.isExecutable() && prefs.get("browser.download.manager.alertOnEXEOpen", true))
+ this.list.modules.commandline.input("This will launch an executable download. Continue? (yes/[no]) ",
+ function (resp) {
+ if (resp && resp.match(/^y(es)?$/i))
+ action.call(self);
+ });
+ else
+ action.call(this);
}
},
@@ -181,7 +209,7 @@ var DownloadList = Class("DownloadList",
addDownload: function addDownload(id) {
if (!(id in this.downloads)) {
- this.downloads[id] = Download(id, this.document);
+ this.downloads[id] = Download(id, this);
let index = values(this.downloads).sort(function (a, b) a.compare(b))
.indexOf(this.downloads[id]);
diff --git a/common/modules/services.jsm b/common/modules/services.jsm
index 74cac15e..1f39cf2c 100644
--- a/common/modules/services.jsm
+++ b/common/modules/services.jsm
@@ -38,6 +38,7 @@ var Services = Module("Services", {
this.add("downloadManager", "@mozilla.org/download-manager;1", Ci.nsIDownloadManager);
this.add("environment", "@mozilla.org/process/environment;1", Ci.nsIEnvironment);
this.add("extensionManager", "@mozilla.org/extensions/manager;1", Ci.nsIExtensionManager);
+ this.add("externalProtocol", "@mozilla.org/uriloader/external-protocol-service;1", Ci.nsIExternalProtocolService);
this.add("favicon", "@mozilla.org/browser/favicon-service;1", Ci.nsIFaviconService);
this.add("focus", "@mozilla.org/focus-manager;1", Ci.nsIFocusManager);
this.add("fuel", "@mozilla.org/fuel/application;1", Ci.extIApplication);