1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 1.2 +++ b/mobile/android/chrome/content/PluginHelper.js Wed Dec 31 06:09:35 2014 +0100 1.3 @@ -0,0 +1,292 @@ 1.4 +/* This Source Code Form is subject to the terms of the Mozilla Public 1.5 + * License, v. 2.0. If a copy of the MPL was not distributed with this file, 1.6 + * You can obtain one at http://mozilla.org/MPL/2.0/. */ 1.7 +"use strict"; 1.8 + 1.9 +var PluginHelper = { 1.10 + showDoorHanger: function(aTab) { 1.11 + if (!aTab.browser) 1.12 + return; 1.13 + 1.14 + // Even though we may not end up showing a doorhanger, this flag 1.15 + // lets us know that we've tried to show a doorhanger. 1.16 + aTab.shouldShowPluginDoorhanger = false; 1.17 + 1.18 + let uri = aTab.browser.currentURI; 1.19 + 1.20 + // If the user has previously set a plugins permission for this website, 1.21 + // either play or don't play the plugins instead of showing a doorhanger. 1.22 + let permValue = Services.perms.testPermission(uri, "plugins"); 1.23 + if (permValue != Services.perms.UNKNOWN_ACTION) { 1.24 + if (permValue == Services.perms.ALLOW_ACTION) 1.25 + PluginHelper.playAllPlugins(aTab.browser.contentWindow); 1.26 + 1.27 + return; 1.28 + } 1.29 + 1.30 + let message = Strings.browser.formatStringFromName("clickToPlayPlugins.message2", 1.31 + [uri.host], 1); 1.32 + let buttons = [ 1.33 + { 1.34 + label: Strings.browser.GetStringFromName("clickToPlayPlugins.activate"), 1.35 + callback: function(aChecked) { 1.36 + // If the user checked "Don't ask again", make a permanent exception 1.37 + if (aChecked) 1.38 + Services.perms.add(uri, "plugins", Ci.nsIPermissionManager.ALLOW_ACTION); 1.39 + 1.40 + PluginHelper.playAllPlugins(aTab.browser.contentWindow); 1.41 + } 1.42 + }, 1.43 + { 1.44 + label: Strings.browser.GetStringFromName("clickToPlayPlugins.dontActivate"), 1.45 + callback: function(aChecked) { 1.46 + // If the user checked "Don't ask again", make a permanent exception 1.47 + if (aChecked) 1.48 + Services.perms.add(uri, "plugins", Ci.nsIPermissionManager.DENY_ACTION); 1.49 + 1.50 + // Other than that, do nothing 1.51 + } 1.52 + } 1.53 + ]; 1.54 + 1.55 + // Add a checkbox with a "Don't ask again" message if the uri contains a 1.56 + // host. Adding a permanent exception will fail if host is not present. 1.57 + let options = uri.host ? { checkbox: Strings.browser.GetStringFromName("clickToPlayPlugins.dontAskAgain") } : {}; 1.58 + 1.59 + NativeWindow.doorhanger.show(message, "ask-to-play-plugins", buttons, aTab.id, options); 1.60 + }, 1.61 + 1.62 + delayAndShowDoorHanger: function(aTab) { 1.63 + // To avoid showing the doorhanger if there are also visible plugin 1.64 + // overlays on the page, delay showing the doorhanger to check if 1.65 + // visible plugins get added in the near future. 1.66 + if (!aTab.pluginDoorhangerTimeout) { 1.67 + aTab.pluginDoorhangerTimeout = setTimeout(function() { 1.68 + if (this.shouldShowPluginDoorhanger) { 1.69 + PluginHelper.showDoorHanger(this); 1.70 + } 1.71 + }.bind(aTab), 500); 1.72 + } 1.73 + }, 1.74 + 1.75 + playAllPlugins: function(aContentWindow) { 1.76 + let cwu = aContentWindow.QueryInterface(Ci.nsIInterfaceRequestor) 1.77 + .getInterface(Ci.nsIDOMWindowUtils); 1.78 + // XXX not sure if we should enable plugins for the parent documents... 1.79 + let plugins = cwu.plugins; 1.80 + if (!plugins || !plugins.length) 1.81 + return; 1.82 + 1.83 + plugins.forEach(this.playPlugin); 1.84 + }, 1.85 + 1.86 + playPlugin: function(plugin) { 1.87 + let objLoadingContent = plugin.QueryInterface(Ci.nsIObjectLoadingContent); 1.88 + if (!objLoadingContent.activated) 1.89 + objLoadingContent.playPlugin(); 1.90 + }, 1.91 + 1.92 + stopPlayPreview: function(plugin, playPlugin) { 1.93 + let objLoadingContent = plugin.QueryInterface(Ci.nsIObjectLoadingContent); 1.94 + if (objLoadingContent.activated) 1.95 + return; 1.96 + 1.97 + if (playPlugin) 1.98 + objLoadingContent.playPlugin(); 1.99 + else 1.100 + objLoadingContent.cancelPlayPreview(); 1.101 + }, 1.102 + 1.103 + getPluginPreference: function getPluginPreference() { 1.104 + let pluginDisable = Services.prefs.getBoolPref("plugin.disable"); 1.105 + if (pluginDisable) 1.106 + return "0"; 1.107 + 1.108 + let state = Services.prefs.getIntPref("plugin.default.state"); 1.109 + return state == Ci.nsIPluginTag.STATE_CLICKTOPLAY ? "2" : "1"; 1.110 + }, 1.111 + 1.112 + setPluginPreference: function setPluginPreference(aValue) { 1.113 + switch (aValue) { 1.114 + case "0": // Enable Plugins = No 1.115 + Services.prefs.setBoolPref("plugin.disable", true); 1.116 + Services.prefs.clearUserPref("plugin.default.state"); 1.117 + break; 1.118 + case "1": // Enable Plugins = Yes 1.119 + Services.prefs.clearUserPref("plugin.disable"); 1.120 + Services.prefs.setIntPref("plugin.default.state", Ci.nsIPluginTag.STATE_ENABLED); 1.121 + break; 1.122 + case "2": // Enable Plugins = Tap to Play (default) 1.123 + Services.prefs.clearUserPref("plugin.disable"); 1.124 + Services.prefs.clearUserPref("plugin.default.state"); 1.125 + break; 1.126 + } 1.127 + }, 1.128 + 1.129 + // Copied from /browser/base/content/browser.js 1.130 + isTooSmall : function (plugin, overlay) { 1.131 + // Is the <object>'s size too small to hold what we want to show? 1.132 + let pluginRect = plugin.getBoundingClientRect(); 1.133 + // XXX bug 446693. The text-shadow on the submitted-report text at 1.134 + // the bottom causes scrollHeight to be larger than it should be. 1.135 + let overflows = (overlay.scrollWidth > pluginRect.width) || 1.136 + (overlay.scrollHeight - 5 > pluginRect.height); 1.137 + 1.138 + return overflows; 1.139 + }, 1.140 + 1.141 + getPluginMimeType: function (plugin) { 1.142 + var tagMimetype = plugin.actualType; 1.143 + 1.144 + if (tagMimetype == "") { 1.145 + tagMimetype = plugin.type; 1.146 + } 1.147 + 1.148 + return tagMimetype; 1.149 + }, 1.150 + 1.151 + handlePluginBindingAttached: function (aTab, aEvent) { 1.152 + let plugin = aEvent.target; 1.153 + let doc = plugin.ownerDocument; 1.154 + let overlay = doc.getAnonymousElementByAttribute(plugin, "anonid", "main"); 1.155 + if (!overlay || overlay._bindingHandled) { 1.156 + return; 1.157 + } 1.158 + overlay._bindingHandled = true; 1.159 + 1.160 + let eventType = PluginHelper._getBindingType(plugin); 1.161 + if (!eventType) { 1.162 + // Not all bindings have handlers 1.163 + return; 1.164 + } 1.165 + 1.166 + switch (eventType) { 1.167 + case "PluginClickToPlay": { 1.168 + // Check if plugins have already been activated for this page, or if 1.169 + // the user has set a permission to always play plugins on the site 1.170 + if (aTab.clickToPlayPluginsActivated || 1.171 + Services.perms.testPermission(aTab.browser.currentURI, "plugins") == 1.172 + Services.perms.ALLOW_ACTION) { 1.173 + PluginHelper.playPlugin(plugin); 1.174 + return; 1.175 + } 1.176 + 1.177 + // If the plugin is hidden, or if the overlay is too small, show a 1.178 + // doorhanger notification 1.179 + if (PluginHelper.isTooSmall(plugin, overlay)) { 1.180 + PluginHelper.delayAndShowDoorHanger(aTab); 1.181 + } else { 1.182 + // There's a large enough visible overlay that we don't need to show 1.183 + // the doorhanger. 1.184 + aTab.shouldShowPluginDoorhanger = false; 1.185 + overlay.classList.add("visible"); 1.186 + } 1.187 + 1.188 + // Add click to play listener to the overlay 1.189 + overlay.addEventListener("click", function(e) { 1.190 + if (!e.isTrusted) 1.191 + return; 1.192 + e.preventDefault(); 1.193 + let win = e.target.ownerDocument.defaultView.top; 1.194 + let tab = BrowserApp.getTabForWindow(win); 1.195 + tab.clickToPlayPluginsActivated = true; 1.196 + PluginHelper.playAllPlugins(win); 1.197 + 1.198 + NativeWindow.doorhanger.hide("ask-to-play-plugins", tab.id); 1.199 + }, true); 1.200 + 1.201 + // Add handlers for over- and underflow in case the plugin gets resized 1.202 + plugin.addEventListener("overflow", function(event) { 1.203 + overlay.classList.remove("visible"); 1.204 + PluginHelper.delayAndShowDoorHanger(aTab); 1.205 + }); 1.206 + plugin.addEventListener("underflow", function(event) { 1.207 + // This is also triggered if only one dimension underflows, 1.208 + // the other dimension might still overflow 1.209 + if (!PluginHelper.isTooSmall(plugin, overlay)) { 1.210 + overlay.classList.add("visible"); 1.211 + } 1.212 + }); 1.213 + 1.214 + break; 1.215 + } 1.216 + 1.217 + case "PluginPlayPreview": { 1.218 + let previewContent = doc.getAnonymousElementByAttribute(plugin, "class", "previewPluginContent"); 1.219 + let pluginHost = Cc["@mozilla.org/plugin/host;1"].getService(Ci.nsIPluginHost); 1.220 + let mimeType = PluginHelper.getPluginMimeType(plugin); 1.221 + let playPreviewInfo = pluginHost.getPlayPreviewInfo(mimeType); 1.222 + 1.223 + if (!playPreviewInfo.ignoreCTP) { 1.224 + // Check if plugins have already been activated for this page, or if 1.225 + // the user has set a permission to always play plugins on the site 1.226 + if (aTab.clickToPlayPluginsActivated || 1.227 + Services.perms.testPermission(aTab.browser.currentURI, "plugins") == 1.228 + Services.perms.ALLOW_ACTION) { 1.229 + PluginHelper.playPlugin(plugin); 1.230 + return; 1.231 + } 1.232 + 1.233 + // Always show door hanger for play preview plugins 1.234 + PluginHelper.delayAndShowDoorHanger(aTab); 1.235 + } 1.236 + 1.237 + let iframe = previewContent.getElementsByClassName("previewPluginContentFrame")[0]; 1.238 + if (!iframe) { 1.239 + // lazy initialization of the iframe 1.240 + iframe = doc.createElementNS("http://www.w3.org/1999/xhtml", "iframe"); 1.241 + iframe.className = "previewPluginContentFrame"; 1.242 + previewContent.appendChild(iframe); 1.243 + } 1.244 + iframe.src = playPreviewInfo.redirectURL; 1.245 + 1.246 + // MozPlayPlugin event can be dispatched from the extension chrome 1.247 + // code to replace the preview content with the native plugin 1.248 + previewContent.addEventListener("MozPlayPlugin", function playPluginHandler(e) { 1.249 + if (!e.isTrusted) 1.250 + return; 1.251 + 1.252 + previewContent.removeEventListener("MozPlayPlugin", playPluginHandler, true); 1.253 + 1.254 + let playPlugin = !aEvent.detail; 1.255 + PluginHelper.stopPlayPreview(plugin, playPlugin); 1.256 + 1.257 + // cleaning up: removes overlay iframe from the DOM 1.258 + let iframe = previewContent.getElementsByClassName("previewPluginContentFrame")[0]; 1.259 + if (iframe) 1.260 + previewContent.removeChild(iframe); 1.261 + }, true); 1.262 + break; 1.263 + } 1.264 + 1.265 + case "PluginNotFound": { 1.266 + // On devices where we don't support Flash, there will be a 1.267 + // "Learn More..." link in the missing plugin error message. 1.268 + let learnMoreLink = doc.getAnonymousElementByAttribute(plugin, "class", "unsupportedLearnMoreLink"); 1.269 + let learnMoreUrl = Services.urlFormatter.formatURLPref("app.support.baseURL"); 1.270 + learnMoreUrl += "mobile-flash-unsupported"; 1.271 + learnMoreLink.href = learnMoreUrl; 1.272 + overlay.classList.add("visible"); 1.273 + break; 1.274 + } 1.275 + } 1.276 + }, 1.277 + 1.278 + // Helper to get the binding handler type from a plugin object 1.279 + _getBindingType: function(plugin) { 1.280 + if (!(plugin instanceof Ci.nsIObjectLoadingContent)) 1.281 + return null; 1.282 + 1.283 + switch (plugin.pluginFallbackType) { 1.284 + case Ci.nsIObjectLoadingContent.PLUGIN_UNSUPPORTED: 1.285 + return "PluginNotFound"; 1.286 + case Ci.nsIObjectLoadingContent.PLUGIN_CLICK_TO_PLAY: 1.287 + return "PluginClickToPlay"; 1.288 + case Ci.nsIObjectLoadingContent.PLUGIN_PLAY_PREVIEW: 1.289 + return "PluginPlayPreview"; 1.290 + default: 1.291 + // Not all states map to a handler 1.292 + return null; 1.293 + } 1.294 + } 1.295 +};