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.

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

mercurial