Wed, 31 Dec 2014 06:09:35 +0100
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 | }; |