mobile/android/components/HelperAppDialog.js

Wed, 31 Dec 2014 06:09:35 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Wed, 31 Dec 2014 06:09:35 +0100
changeset 0
6474c204b198
permissions
-rw-r--r--

Cloned upstream origin tor-browser at tor-browser-31.3.0esr-4.5-1-build1
revision ID fc1c9ff7c1b2defdbc039f12214767608f46423f for hacking purpose.

michael@0 1 // -*- Mode: js2; tab-width: 2; indent-tabs-mode: nil; js2-basic-offset: 2; js2-skip-preprocessor-directives: t; -*-
michael@0 2 /* This Source Code Form is subject to the terms of the Mozilla Public
michael@0 3 * License, v. 2.0. If a copy of the MPL was not distributed with this
michael@0 4 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
michael@0 5
michael@0 6 const { classes: Cc, interfaces: Ci, utils: Cu, results: Cr } = Components;
michael@0 7
michael@0 8 const APK_MIME_TYPE = "application/vnd.android.package-archive";
michael@0 9 const PREF_BD_USEDOWNLOADDIR = "browser.download.useDownloadDir";
michael@0 10 const URI_GENERIC_ICON_DOWNLOAD = "drawable://alert_download";
michael@0 11
michael@0 12 Cu.import("resource://gre/modules/FileUtils.jsm");
michael@0 13 Cu.import("resource://gre/modules/HelperApps.jsm");
michael@0 14 Cu.import("resource://gre/modules/Prompt.jsm");
michael@0 15 Cu.import("resource://gre/modules/Services.jsm");
michael@0 16 Cu.import("resource://gre/modules/XPCOMUtils.jsm");
michael@0 17
michael@0 18 // -----------------------------------------------------------------------
michael@0 19 // HelperApp Launcher Dialog
michael@0 20 // -----------------------------------------------------------------------
michael@0 21
michael@0 22 function HelperAppLauncherDialog() { }
michael@0 23
michael@0 24 HelperAppLauncherDialog.prototype = {
michael@0 25 classID: Components.ID("{e9d277a0-268a-4ec2-bb8c-10fdf3e44611}"),
michael@0 26 QueryInterface: XPCOMUtils.generateQI([Ci.nsIHelperAppLauncherDialog]),
michael@0 27
michael@0 28 getNativeWindow: function () {
michael@0 29 try {
michael@0 30 let win = Services.wm.getMostRecentWindow("navigator:browser");
michael@0 31 if (win && win.NativeWindow) {
michael@0 32 return win.NativeWindow;
michael@0 33 }
michael@0 34 } catch (e) {
michael@0 35 }
michael@0 36 return null;
michael@0 37 },
michael@0 38
michael@0 39 /**
michael@0 40 * Returns false if `url` represents a local or special URL that we don't
michael@0 41 * wish to ever download.
michael@0 42 *
michael@0 43 * Returns true otherwise.
michael@0 44 */
michael@0 45 _canDownload: function (url, alreadyResolved=false) {
michael@0 46 // The common case.
michael@0 47 if (url.schemeIs("http") ||
michael@0 48 url.schemeIs("https") ||
michael@0 49 url.schemeIs("ftp")) {
michael@0 50 return true;
michael@0 51 }
michael@0 52
michael@0 53 // The less-common opposite case.
michael@0 54 if (url.schemeIs("chrome") ||
michael@0 55 url.schemeIs("jar") ||
michael@0 56 url.schemeIs("resource") ||
michael@0 57 url.schemeIs("wyciwyg")) {
michael@0 58 return false;
michael@0 59 }
michael@0 60
michael@0 61 // For all other URIs, try to resolve them to an inner URI, and check that.
michael@0 62 if (!alreadyResolved) {
michael@0 63 let ioSvc = Cc["@mozilla.org/network/io-service;1"].getService(Components.interfaces.nsIIOService);
michael@0 64 let innerURI = ioSvc.newChannelFromURI(url).URI;
michael@0 65 if (!url.equals(innerURI)) {
michael@0 66 return this._canDownload(innerURI, true);
michael@0 67 }
michael@0 68 }
michael@0 69
michael@0 70 if (url.schemeIs("file")) {
michael@0 71 // If it's in our app directory or profile directory, we never ever
michael@0 72 // want to do anything with it, including saving to disk or passing the
michael@0 73 // file to another application.
michael@0 74 let file = url.QueryInterface(Ci.nsIFileURL).file;
michael@0 75
michael@0 76 // Normalize the nsILocalFile in-place. This will ensure that paths
michael@0 77 // can be correctly compared via `contains`, below.
michael@0 78 file.normalize();
michael@0 79
michael@0 80 // TODO: pref blacklist?
michael@0 81
michael@0 82 let appRoot = FileUtils.getFile("XREExeF", []);
michael@0 83 if (appRoot.contains(file, true)) {
michael@0 84 return false;
michael@0 85 }
michael@0 86
michael@0 87 let profileRoot = FileUtils.getFile("ProfD", []);
michael@0 88 if (profileRoot.contains(file, true)) {
michael@0 89 return false;
michael@0 90 }
michael@0 91
michael@0 92 return true;
michael@0 93 }
michael@0 94
michael@0 95 // Anything else is fine to download.
michael@0 96 return true;
michael@0 97 },
michael@0 98
michael@0 99 /**
michael@0 100 * Returns true if `launcher` represents a download for which we wish
michael@0 101 * to prompt.
michael@0 102 */
michael@0 103 _shouldPrompt: function (launcher) {
michael@0 104 let mimeType = this._getMimeTypeFromLauncher(launcher);
michael@0 105
michael@0 106 // Straight equality: nsIMIMEInfo normalizes.
michael@0 107 return APK_MIME_TYPE == mimeType;
michael@0 108 },
michael@0 109
michael@0 110 show: function hald_show(aLauncher, aContext, aReason) {
michael@0 111 if (!this._canDownload(aLauncher.source)) {
michael@0 112 aLauncher.cancel(Cr.NS_BINDING_ABORTED);
michael@0 113
michael@0 114 let win = this.getNativeWindow();
michael@0 115 if (!win) {
michael@0 116 // Oops.
michael@0 117 Services.console.logStringMessage("Refusing download, but can't show a toast.");
michael@0 118 return;
michael@0 119 }
michael@0 120
michael@0 121 Services.console.logStringMessage("Refusing download of non-downloadable file.");
michael@0 122 let bundle = Services.strings.createBundle("chrome://browser/locale/handling.properties");
michael@0 123 let failedText = bundle.GetStringFromName("protocol.failed");
michael@0 124 win.toast.show(failedText, "long");
michael@0 125
michael@0 126 return;
michael@0 127 }
michael@0 128
michael@0 129 let bundle = Services.strings.createBundle("chrome://browser/locale/browser.properties");
michael@0 130
michael@0 131 let defaultHandler = new Object();
michael@0 132 let apps = HelperApps.getAppsForUri(aLauncher.source, {
michael@0 133 mimeType: aLauncher.MIMEInfo.MIMEType,
michael@0 134 });
michael@0 135
michael@0 136 // Add a fake intent for save to disk at the top of the list.
michael@0 137 apps.unshift({
michael@0 138 name: bundle.GetStringFromName("helperapps.saveToDisk"),
michael@0 139 packageName: "org.mozilla.gecko.Download",
michael@0 140 iconUri: "drawable://icon",
michael@0 141 launch: function() {
michael@0 142 // Reset the preferredAction here.
michael@0 143 aLauncher.MIMEInfo.preferredAction = Ci.nsIMIMEInfo.saveToDisk;
michael@0 144 aLauncher.saveToDisk(null, false);
michael@0 145 return true;
michael@0 146 }
michael@0 147 });
michael@0 148
michael@0 149 // See if the user already marked something as the default for this mimetype,
michael@0 150 // and if that app is still installed.
michael@0 151 let preferredApp = this._getPreferredApp(aLauncher);
michael@0 152 if (preferredApp) {
michael@0 153 let pref = apps.filter(function(app) {
michael@0 154 return app.packageName === preferredApp;
michael@0 155 });
michael@0 156
michael@0 157 if (pref.length > 0) {
michael@0 158 pref[0].launch(aLauncher.source);
michael@0 159 return;
michael@0 160 }
michael@0 161 }
michael@0 162
michael@0 163 let callback = function(app) {
michael@0 164 aLauncher.MIMEInfo.preferredAction = Ci.nsIMIMEInfo.useHelperApp;
michael@0 165 if (!app.launch(aLauncher.source)) {
michael@0 166 aLauncher.cancel(Cr.NS_BINDING_ABORTED);
michael@0 167 }
michael@0 168 }
michael@0 169
michael@0 170 // If there's only one choice, and we don't want to prompt, go right ahead
michael@0 171 // and choose that app automatically.
michael@0 172 if (!this._shouldPrompt(aLauncher) && (apps.length === 1)) {
michael@0 173 callback(apps[0]);
michael@0 174 return;
michael@0 175 }
michael@0 176
michael@0 177 // Otherwise, let's go through the prompt.
michael@0 178 HelperApps.prompt(apps, {
michael@0 179 title: bundle.GetStringFromName("helperapps.pick"),
michael@0 180 buttons: [
michael@0 181 bundle.GetStringFromName("helperapps.alwaysUse"),
michael@0 182 bundle.GetStringFromName("helperapps.useJustOnce")
michael@0 183 ]
michael@0 184 }, (data) => {
michael@0 185 if (data.button < 0) {
michael@0 186 return;
michael@0 187 }
michael@0 188
michael@0 189 callback(apps[data.icongrid0]);
michael@0 190
michael@0 191 if (data.button === 0) {
michael@0 192 this._setPreferredApp(aLauncher, apps[data.icongrid0]);
michael@0 193 }
michael@0 194 });
michael@0 195 },
michael@0 196
michael@0 197 _getPrefName: function getPrefName(mimetype) {
michael@0 198 return "browser.download.preferred." + mimetype.replace("\\", ".");
michael@0 199 },
michael@0 200
michael@0 201 _getMimeTypeFromLauncher: function (launcher) {
michael@0 202 let mime = launcher.MIMEInfo.MIMEType;
michael@0 203 if (!mime)
michael@0 204 mime = ContentAreaUtils.getMIMETypeForURI(launcher.source) || "";
michael@0 205 return mime;
michael@0 206 },
michael@0 207
michael@0 208 _getPreferredApp: function getPreferredApp(launcher) {
michael@0 209 let mime = this._getMimeTypeFromLauncher(launcher);
michael@0 210 if (!mime)
michael@0 211 return;
michael@0 212
michael@0 213 try {
michael@0 214 return Services.prefs.getCharPref(this._getPrefName(mime));
michael@0 215 } catch(ex) {
michael@0 216 Services.console.logStringMessage("Error getting pref for " + mime + ".");
michael@0 217 }
michael@0 218 return null;
michael@0 219 },
michael@0 220
michael@0 221 _setPreferredApp: function setPreferredApp(launcher, app) {
michael@0 222 let mime = this._getMimeTypeFromLauncher(launcher);
michael@0 223 if (!mime)
michael@0 224 return;
michael@0 225
michael@0 226 if (app)
michael@0 227 Services.prefs.setCharPref(this._getPrefName(mime), app.packageName);
michael@0 228 else
michael@0 229 Services.prefs.clearUserPref(this._getPrefName(mime));
michael@0 230 },
michael@0 231
michael@0 232 promptForSaveToFile: function hald_promptForSaveToFile(aLauncher, aContext, aDefaultFile, aSuggestedFileExt, aForcePrompt) {
michael@0 233 // Retrieve the user's default download directory
michael@0 234 let dnldMgr = Cc["@mozilla.org/download-manager;1"].getService(Ci.nsIDownloadManager);
michael@0 235 let defaultFolder = dnldMgr.userDownloadsDirectory;
michael@0 236
michael@0 237 try {
michael@0 238 file = this.validateLeafName(defaultFolder, aDefaultFile, aSuggestedFileExt);
michael@0 239 } catch (e) { }
michael@0 240
michael@0 241 return file;
michael@0 242 },
michael@0 243
michael@0 244 validateLeafName: function hald_validateLeafName(aLocalFile, aLeafName, aFileExt) {
michael@0 245 if (!(aLocalFile && this.isUsableDirectory(aLocalFile)))
michael@0 246 return null;
michael@0 247
michael@0 248 // Remove any leading periods, since we don't want to save hidden files
michael@0 249 // automatically.
michael@0 250 aLeafName = aLeafName.replace(/^\.+/, "");
michael@0 251
michael@0 252 if (aLeafName == "")
michael@0 253 aLeafName = "unnamed" + (aFileExt ? "." + aFileExt : "");
michael@0 254 aLocalFile.append(aLeafName);
michael@0 255
michael@0 256 this.makeFileUnique(aLocalFile);
michael@0 257 return aLocalFile;
michael@0 258 },
michael@0 259
michael@0 260 makeFileUnique: function hald_makeFileUnique(aLocalFile) {
michael@0 261 try {
michael@0 262 // Note - this code is identical to that in
michael@0 263 // toolkit/content/contentAreaUtils.js.
michael@0 264 // If you are updating this code, update that code too! We can't share code
michael@0 265 // here since this is called in a js component.
michael@0 266 let collisionCount = 0;
michael@0 267 while (aLocalFile.exists()) {
michael@0 268 collisionCount++;
michael@0 269 if (collisionCount == 1) {
michael@0 270 // Append "(2)" before the last dot in (or at the end of) the filename
michael@0 271 // special case .ext.gz etc files so we don't wind up with .tar(2).gz
michael@0 272 if (aLocalFile.leafName.match(/\.[^\.]{1,3}\.(gz|bz2|Z)$/i))
michael@0 273 aLocalFile.leafName = aLocalFile.leafName.replace(/\.[^\.]{1,3}\.(gz|bz2|Z)$/i, "(2)$&");
michael@0 274 else
michael@0 275 aLocalFile.leafName = aLocalFile.leafName.replace(/(\.[^\.]*)?$/, "(2)$&");
michael@0 276 }
michael@0 277 else {
michael@0 278 // replace the last (n) in the filename with (n+1)
michael@0 279 aLocalFile.leafName = aLocalFile.leafName.replace(/^(.*\()\d+\)/, "$1" + (collisionCount+1) + ")");
michael@0 280 }
michael@0 281 }
michael@0 282 aLocalFile.create(Ci.nsIFile.NORMAL_FILE_TYPE, 0600);
michael@0 283 }
michael@0 284 catch (e) {
michael@0 285 dump("*** exception in validateLeafName: " + e + "\n");
michael@0 286
michael@0 287 if (e.result == Cr.NS_ERROR_FILE_ACCESS_DENIED)
michael@0 288 throw e;
michael@0 289
michael@0 290 if (aLocalFile.leafName == "" || aLocalFile.isDirectory()) {
michael@0 291 aLocalFile.append("unnamed");
michael@0 292 if (aLocalFile.exists())
michael@0 293 aLocalFile.createUnique(Ci.nsIFile.NORMAL_FILE_TYPE, 0600);
michael@0 294 }
michael@0 295 }
michael@0 296 },
michael@0 297
michael@0 298 isUsableDirectory: function hald_isUsableDirectory(aDirectory) {
michael@0 299 return aDirectory.exists() && aDirectory.isDirectory() && aDirectory.isWritable();
michael@0 300 },
michael@0 301 };
michael@0 302
michael@0 303 this.NSGetFactory = XPCOMUtils.generateNSGetFactory([HelperAppLauncherDialog]);

mercurial