mobile/android/modules/HelperApps.jsm

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 /* This Source Code Form is subject to the terms of the Mozilla Public
michael@0 2 * License, v. 2.0. If a copy of the MPL was not distributed with this file,
michael@0 3 * You can obtain one at http://mozilla.org/MPL/2.0/. */
michael@0 4 "use strict";
michael@0 5
michael@0 6 const { classes: Cc, interfaces: Ci, utils: Cu, results: Cr } = Components;
michael@0 7
michael@0 8 Cu.import("resource://gre/modules/XPCOMUtils.jsm");
michael@0 9 Cu.import("resource://gre/modules/Services.jsm");
michael@0 10 Cu.import("resource://gre/modules/Prompt.jsm");
michael@0 11 Cu.import("resource://gre/modules/Messaging.jsm");
michael@0 12
michael@0 13 XPCOMUtils.defineLazyGetter(this, "ContentAreaUtils", function() {
michael@0 14 let ContentAreaUtils = {};
michael@0 15 Services.scriptloader.loadSubScript("chrome://global/content/contentAreaUtils.js", ContentAreaUtils);
michael@0 16 return ContentAreaUtils;
michael@0 17 });
michael@0 18
michael@0 19 this.EXPORTED_SYMBOLS = ["App","HelperApps"];
michael@0 20
michael@0 21 function App(data) {
michael@0 22 this.name = data.name;
michael@0 23 this.isDefault = data.isDefault;
michael@0 24 this.packageName = data.packageName;
michael@0 25 this.activityName = data.activityName;
michael@0 26 this.iconUri = "-moz-icon://" + data.packageName;
michael@0 27 }
michael@0 28
michael@0 29 App.prototype = {
michael@0 30 // callback will be null if a result is not requested
michael@0 31 launch: function(uri, callback) {
michael@0 32 HelperApps._launchApp(this, uri, callback);
michael@0 33 return false;
michael@0 34 }
michael@0 35 }
michael@0 36
michael@0 37 var HelperApps = {
michael@0 38 get defaultBrowsers() {
michael@0 39 delete this.defaultBrowsers;
michael@0 40 this.defaultBrowsers = this._getHandlers("http://www.example.com", {
michael@0 41 filterBrowsers: false,
michael@0 42 filterHtml: false
michael@0 43 });
michael@0 44 return this.defaultBrowsers;
michael@0 45 },
michael@0 46
michael@0 47 // Finds handlers that have registered for text/html pages or urls ending in html. Some apps, like
michael@0 48 // the Samsung Video player will only appear for these urls, while some Browsers (like Link Bubble)
michael@0 49 // won't register here because of the text/html mime type.
michael@0 50 get defaultHtmlHandlers() {
michael@0 51 delete this.defaultHtmlHandlers;
michael@0 52 return this.defaultHtmlHandlers = this._getHandlers("http://www.example.com/index.html", {
michael@0 53 filterBrowsers: false,
michael@0 54 filterHtml: false
michael@0 55 });
michael@0 56 },
michael@0 57
michael@0 58 _getHandlers: function(url, options) {
michael@0 59 let values = {};
michael@0 60
michael@0 61 let handlers = this.getAppsForUri(Services.io.newURI(url, null, null), options);
michael@0 62 handlers.forEach(function(app) {
michael@0 63 values[app.name] = app;
michael@0 64 }, this);
michael@0 65
michael@0 66 return values;
michael@0 67 },
michael@0 68
michael@0 69 get protoSvc() {
michael@0 70 delete this.protoSvc;
michael@0 71 return this.protoSvc = Cc["@mozilla.org/uriloader/external-protocol-service;1"].getService(Ci.nsIExternalProtocolService);
michael@0 72 },
michael@0 73
michael@0 74 get urlHandlerService() {
michael@0 75 delete this.urlHandlerService;
michael@0 76 return this.urlHandlerService = Cc["@mozilla.org/uriloader/external-url-handler-service;1"].getService(Ci.nsIExternalURLHandlerService);
michael@0 77 },
michael@0 78
michael@0 79 prompt: function showPicker(apps, promptOptions, callback) {
michael@0 80 let p = new Prompt(promptOptions).addIconGrid({ items: apps });
michael@0 81 p.show(callback);
michael@0 82 },
michael@0 83
michael@0 84 getAppsForProtocol: function getAppsForProtocol(scheme) {
michael@0 85 let protoHandlers = this.protoSvc.getProtocolHandlerInfoFromOS(scheme, {}).possibleApplicationHandlers;
michael@0 86
michael@0 87 let results = {};
michael@0 88 for (let i = 0; i < protoHandlers.length; i++) {
michael@0 89 try {
michael@0 90 let protoApp = protoHandlers.queryElementAt(i, Ci.nsIHandlerApp);
michael@0 91 results[protoApp.name] = new App({
michael@0 92 name: protoApp.name,
michael@0 93 description: protoApp.detailedDescription,
michael@0 94 });
michael@0 95 } catch(e) {}
michael@0 96 }
michael@0 97
michael@0 98 return results;
michael@0 99 },
michael@0 100
michael@0 101 getAppsForUri: function getAppsForUri(uri, flags = { }, callback) {
michael@0 102 flags.filterBrowsers = "filterBrowsers" in flags ? flags.filterBrowsers : true;
michael@0 103 flags.filterHtml = "filterHtml" in flags ? flags.filterHtml : true;
michael@0 104
michael@0 105 // Query for apps that can/can't handle the mimetype
michael@0 106 let msg = this._getMessage("Intent:GetHandlers", uri, flags);
michael@0 107 let parseData = (d) => {
michael@0 108 let apps = []
michael@0 109
michael@0 110 if (!d)
michael@0 111 return apps;
michael@0 112
michael@0 113 apps = this._parseApps(d.apps);
michael@0 114
michael@0 115 if (flags.filterBrowsers) {
michael@0 116 apps = apps.filter((app) => {
michael@0 117 return app.name && !this.defaultBrowsers[app.name];
michael@0 118 });
michael@0 119 }
michael@0 120
michael@0 121 // Some apps will register for html files (the Samsung Video player) but should be shown
michael@0 122 // for non-HTML files (like videos). This filters them only if the page has an htm of html
michael@0 123 // file extension.
michael@0 124 if (flags.filterHtml) {
michael@0 125 // Matches from the first '.' to the end of the string, '?', or '#'
michael@0 126 let ext = /\.([^\?#]*)/.exec(uri.path);
michael@0 127 if (ext && (ext[1] === "html" || ext[1] === "htm")) {
michael@0 128 apps = apps.filter(function(app) {
michael@0 129 return app.name && !this.defaultHtmlHandlers[app.name];
michael@0 130 }, this);
michael@0 131 }
michael@0 132 }
michael@0 133
michael@0 134 return apps;
michael@0 135 };
michael@0 136
michael@0 137 if (!callback) {
michael@0 138 let data = this._sendMessageSync(msg);
michael@0 139 if (!data)
michael@0 140 return [];
michael@0 141 return parseData(data);
michael@0 142 } else {
michael@0 143 sendMessageToJava(msg, function(data) {
michael@0 144 callback(parseData(data));
michael@0 145 });
michael@0 146 }
michael@0 147 },
michael@0 148
michael@0 149 launchUri: function launchUri(uri) {
michael@0 150 let msg = this._getMessage("Intent:Open", uri);
michael@0 151 sendMessageToJava(msg);
michael@0 152 },
michael@0 153
michael@0 154 _parseApps: function _parseApps(appInfo) {
michael@0 155 // appInfo -> {apps: [app1Label, app1Default, app1PackageName, app1ActivityName, app2Label, app2Defaut, ...]}
michael@0 156 // see GeckoAppShell.java getHandlersForIntent function for details
michael@0 157 const numAttr = 4; // 4 elements per ResolveInfo: label, default, package name, activity name.
michael@0 158
michael@0 159 let apps = [];
michael@0 160 for (let i = 0; i < appInfo.length; i += numAttr) {
michael@0 161 apps.push(new App({"name" : appInfo[i],
michael@0 162 "isDefault" : appInfo[i+1],
michael@0 163 "packageName" : appInfo[i+2],
michael@0 164 "activityName" : appInfo[i+3]}));
michael@0 165 }
michael@0 166
michael@0 167 return apps;
michael@0 168 },
michael@0 169
michael@0 170 _getMessage: function(type, uri, options = {}) {
michael@0 171 let mimeType = options.mimeType;
michael@0 172 if (uri && mimeType == undefined)
michael@0 173 mimeType = ContentAreaUtils.getMIMETypeForURI(uri) || "";
michael@0 174
michael@0 175 return {
michael@0 176 type: type,
michael@0 177 mime: mimeType,
michael@0 178 action: options.action || "", // empty action string defaults to android.intent.action.VIEW
michael@0 179 url: uri ? uri.spec : "",
michael@0 180 packageName: options.packageName || "",
michael@0 181 className: options.className || ""
michael@0 182 };
michael@0 183 },
michael@0 184
michael@0 185 _launchApp: function launchApp(app, uri, callback) {
michael@0 186 if (callback) {
michael@0 187 let msg = this._getMessage("Intent:OpenForResult", uri, {
michael@0 188 packageName: app.packageName,
michael@0 189 className: app.activityName
michael@0 190 });
michael@0 191
michael@0 192 sendMessageToJava(msg, function(data) {
michael@0 193 callback(data);
michael@0 194 });
michael@0 195 } else {
michael@0 196 let msg = this._getMessage("Intent:Open", uri, {
michael@0 197 packageName: app.packageName,
michael@0 198 className: app.activityName
michael@0 199 });
michael@0 200
michael@0 201 sendMessageToJava(msg);
michael@0 202 }
michael@0 203 },
michael@0 204
michael@0 205 _sendMessageSync: function(msg) {
michael@0 206 let res = null;
michael@0 207 sendMessageToJava(msg, function(data) {
michael@0 208 res = data;
michael@0 209 });
michael@0 210
michael@0 211 let thread = Services.tm.currentThread;
michael@0 212 while (res == null)
michael@0 213 thread.processNextEvent(true);
michael@0 214
michael@0 215 return res;
michael@0 216 },
michael@0 217 };

mercurial