123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790 |
- # -*- Mode: javascript; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-
- # This Source Code Form is subject to the terms of the Mozilla Public
- # License, v. 2.0. If a copy of the MPL was not distributed with this
- # file, You can obtain one at http://mozilla.org/MPL/2.0/.
- const kPrefSessionPersistMinutes = "plugin.sessionPermissionNow.intervalInMinutes";
- const kPrefPersistentDays = "plugin.persistentPermissionAlways.intervalInDays";
- var gPluginHandler = {
- PLUGIN_SCRIPTED_STATE_NONE: 0,
- PLUGIN_SCRIPTED_STATE_FIRED: 1,
- PLUGIN_SCRIPTED_STATE_DONE: 2,
- getPluginUI: function(plugin, anonid) {
- return plugin.ownerDocument.
- getAnonymousElementByAttribute(plugin, "anonid", anonid);
- },
- _getPluginInfo: function(pluginElement) {
- let pluginHost = Cc["@mozilla.org/plugin/host;1"].getService(Ci.nsIPluginHost);
- pluginElement.QueryInterface(Ci.nsIObjectLoadingContent);
- let tagMimetype;
- let pluginName = gNavigatorBundle.getString("pluginInfo.unknownPlugin");
- let pluginTag = null;
- let permissionString = null;
- let fallbackType = null;
- let blocklistState = null;
- if (pluginElement instanceof HTMLAppletElement) {
- tagMimetype = "application/x-java-vm";
- } else {
- tagMimetype = pluginElement.actualType;
- if (tagMimetype == "") {
- tagMimetype = pluginElement.type;
- }
- }
- if (gPluginHandler.isKnownPlugin(pluginElement)) {
- pluginTag = pluginHost.getPluginTagForType(pluginElement.actualType);
- pluginName = gPluginHandler.makeNicePluginName(pluginTag.name);
- permissionString = pluginHost.getPermissionStringForType(pluginElement.actualType);
- fallbackType = pluginElement.defaultFallbackType;
- blocklistState = pluginHost.getBlocklistStateForType(pluginElement.actualType);
- // Make state-softblocked == state-notblocked for our purposes,
- // they have the same UI. STATE_OUTDATED should not exist for plugin
- // items, but let's alias it anyway, just in case.
- if (blocklistState == Ci.nsIBlocklistService.STATE_SOFTBLOCKED ||
- blocklistState == Ci.nsIBlocklistService.STATE_OUTDATED) {
- blocklistState = Ci.nsIBlocklistService.STATE_NOT_BLOCKED;
- }
- }
- return { mimetype: tagMimetype,
- pluginName: pluginName,
- pluginTag: pluginTag,
- permissionString: permissionString,
- fallbackType: fallbackType,
- blocklistState: blocklistState,
- };
- },
- // Map the plugin's name to a filtered version more suitable for user UI.
- makeNicePluginName : function(aName) {
- if (aName == "Shockwave Flash") {
- return "Adobe Flash";
- }
- // Clean up the plugin name by stripping off any trailing version numbers
- // or "plugin". EG, "Foo Bar Plugin 1.23_02" --> "Foo Bar"
- // Do this by first stripping the numbers, etc. off the end, and then
- // removing "Plugin" (and then trimming to get rid of any whitespace).
- // (Otherwise, something like "Java(TM) Plug-in 1.7.0_07" gets mangled)
- let newName = aName.replace(/[\s\d\.\-\_\(\)]+$/, "").replace(/\bplug-?in\b/i, "").trim();
- return newName;
- },
- isTooSmall : function(plugin, overlay) {
- // Is the <object>'s size too small to hold what we want to show?
- let pluginRect = plugin.getBoundingClientRect();
- // XXX bug 446693. The text-shadow on the submitted-report text at
- // the bottom causes scrollHeight to be larger than it should be.
- // Clamp width/height to properly show CTP overlay on different
- // zoom levels when embedded in iframes (rounding bug). (Bug 972237)
- let overflows = (overlay.scrollWidth > Math.ceil(pluginRect.width)) ||
- (overlay.scrollHeight - 5 > Math.ceil(pluginRect.height));
- return overflows;
- },
- addLinkClickCallback: function(linkNode, callbackName /*callbackArgs...*/) {
- // XXX just doing (callback)(arg) was giving a same-origin error. bug?
- let self = this;
- let callbackArgs = Array.prototype.slice.call(arguments).slice(2);
- linkNode.addEventListener("click",
- function(evt) {
- if (!evt.isTrusted) {
- return;
- }
- evt.preventDefault();
- if (callbackArgs.length == 0) {
- callbackArgs = [ evt ];
- }
- (self[callbackName]).apply(self, callbackArgs);
- }, true);
- linkNode.addEventListener("keydown",
- function(evt) {
- if (!evt.isTrusted) {
- return;
- }
- if (evt.keyCode == evt.DOM_VK_RETURN) {
- evt.preventDefault();
- if (callbackArgs.length == 0) {
- callbackArgs = [ evt ];
- }
- evt.preventDefault();
- (self[callbackName]).apply(self, callbackArgs);
- }
- }, true);
- },
- // Helper to get the binding handler type from a plugin object
- _getBindingType : function(plugin) {
- if (!(plugin instanceof Ci.nsIObjectLoadingContent)) {
- return null;
- }
- switch (plugin.pluginFallbackType) {
- case Ci.nsIObjectLoadingContent.PLUGIN_UNSUPPORTED:
- return "PluginNotFound";
- case Ci.nsIObjectLoadingContent.PLUGIN_DISABLED:
- return "PluginDisabled";
- case Ci.nsIObjectLoadingContent.PLUGIN_BLOCKLISTED:
- return "PluginBlocklisted";
- case Ci.nsIObjectLoadingContent.PLUGIN_OUTDATED:
- return "PluginOutdated";
- case Ci.nsIObjectLoadingContent.PLUGIN_CLICK_TO_PLAY:
- return "PluginClickToPlay";
- case Ci.nsIObjectLoadingContent.PLUGIN_VULNERABLE_UPDATABLE:
- return "PluginVulnerableUpdatable";
- case Ci.nsIObjectLoadingContent.PLUGIN_VULNERABLE_NO_UPDATE:
- return "PluginVulnerableNoUpdate";
- case Ci.nsIObjectLoadingContent.PLUGIN_PLAY_PREVIEW:
- return "PluginPlayPreview";
- default:
- // Not all states map to a handler
- return null;
- }
- },
- handleEvent : function(event) {
- let plugin;
- let doc;
- let eventType = event.type;
- if (eventType === "PluginRemoved") {
- doc = event.target;
- } else {
- plugin = event.target;
- doc = plugin.ownerDocument;
- if (!(plugin instanceof Ci.nsIObjectLoadingContent)) {
- return;
- }
- }
- if (eventType == "PluginBindingAttached") {
- // The plugin binding fires this event when it is created.
- // As an untrusted event, ensure that this object actually has a binding
- // and make sure we don't handle it twice
- let overlay = this.getPluginUI(plugin, "main");
- if (!overlay || overlay._bindingHandled) {
- return;
- }
- overlay._bindingHandled = true;
- // Lookup the handler for this binding
- eventType = this._getBindingType(plugin);
- if (!eventType) {
- // Not all bindings have handlers
- return;
- }
- }
- let shouldShowNotification = false;
- let browser = gBrowser.getBrowserForDocument(doc.defaultView.top.document);
- if (!browser) {
- return;
- }
- switch (eventType) {
- case "PluginCrashed":
- this.pluginInstanceCrashed(plugin, event);
- break;
- case "PluginNotFound":
- /* No action (plugin finder obsolete) */
- break;
- case "PluginBlocklisted":
- case "PluginOutdated":
- shouldShowNotification = true;
- break;
- case "PluginVulnerableUpdatable":
- let updateLink = this.getPluginUI(plugin, "checkForUpdatesLink");
- this.addLinkClickCallback(updateLink, "openPluginUpdatePage");
- /* FALLTHRU */
- case "PluginVulnerableNoUpdate":
- case "PluginClickToPlay":
- this._handleClickToPlayEvent(plugin);
- let overlay = this.getPluginUI(plugin, "main");
- let pluginName = this._getPluginInfo(plugin).pluginName;
- let messageString = gNavigatorBundle.getFormattedString("PluginClickToActivate", [pluginName]);
- let overlayText = this.getPluginUI(plugin, "clickToPlay");
- overlayText.textContent = messageString;
- if (eventType == "PluginVulnerableUpdatable" ||
- eventType == "PluginVulnerableNoUpdate") {
- let vulnerabilityString = gNavigatorBundle.getString(eventType);
- let vulnerabilityText = this.getPluginUI(plugin, "vulnerabilityStatus");
- vulnerabilityText.textContent = vulnerabilityString;
- }
- shouldShowNotification = true;
- break;
- case "PluginPlayPreview":
- this._handlePlayPreviewEvent(plugin);
- break;
- case "PluginDisabled":
- let manageLink = this.getPluginUI(plugin, "managePluginsLink");
- this.addLinkClickCallback(manageLink, "managePlugins");
- shouldShowNotification = true;
- break;
- case "PluginInstantiated":
- //Pale Moon: don't show the indicator when plugins are enabled/allowed
- if (gPrefService.getBoolPref("plugins.always_show_indicator")) {
- shouldShowNotification = true;
- }
- break;
- case "PluginRemoved":
- shouldShowNotification = true;
- break;
- }
- // Show the in-content UI if it's not too big. The crashed plugin handler already did this.
- if (eventType != "PluginCrashed" && eventType != "PluginRemoved") {
- let overlay = this.getPluginUI(plugin, "main");
- if (overlay != null) {
- if (!this.isTooSmall(plugin, overlay)) {
- overlay.style.visibility = "visible";
- }
- plugin.addEventListener("overflow", function(event) {
- overlay.style.visibility = "hidden";
- });
- plugin.addEventListener("underflow", function(event) {
- // this is triggered if only one dimension underflows,
- // the other dimension might still overflow
- if (!gPluginHandler.isTooSmall(plugin, overlay)) {
- overlay.style.visibility = "visible";
- }
- });
- }
- }
- // Only show the notification after we've done the isTooSmall check, so
- // that the notification can decide whether to show the "alert" icon
- if (shouldShowNotification) {
- this._showClickToPlayNotification(browser);
- }
- },
- isKnownPlugin: function(objLoadingContent) {
- return (objLoadingContent.getContentTypeForMIMEType(objLoadingContent.actualType) ==
- Ci.nsIObjectLoadingContent.TYPE_PLUGIN);
- },
- canActivatePlugin: function(objLoadingContent) {
- // if this isn't a known plugin, we can't activate it
- // (this also guards pluginHost.getPermissionStringForType against
- // unexpected input)
- if (!gPluginHandler.isKnownPlugin(objLoadingContent)) {
- return false;
- }
- let pluginHost = Cc["@mozilla.org/plugin/host;1"].getService(Ci.nsIPluginHost);
- let permissionString = pluginHost.getPermissionStringForType(objLoadingContent.actualType);
- let principal = objLoadingContent.ownerDocument.defaultView.top.document.nodePrincipal;
- let pluginPermission = Services.perms.testPermissionFromPrincipal(principal, permissionString);
- let isFallbackTypeValid =
- objLoadingContent.pluginFallbackType >= Ci.nsIObjectLoadingContent.PLUGIN_CLICK_TO_PLAY &&
- objLoadingContent.pluginFallbackType <= Ci.nsIObjectLoadingContent.PLUGIN_VULNERABLE_NO_UPDATE;
- if (objLoadingContent.pluginFallbackType == Ci.nsIObjectLoadingContent.PLUGIN_PLAY_PREVIEW) {
- // checking if play preview is subject to CTP rules
- let playPreviewInfo = pluginHost.getPlayPreviewInfo(objLoadingContent.actualType);
- isFallbackTypeValid = !playPreviewInfo.ignoreCTP;
- }
- return !objLoadingContent.activated &&
- pluginPermission != Ci.nsIPermissionManager.DENY_ACTION &&
- isFallbackTypeValid;
- },
- hideClickToPlayOverlay: function(aPlugin) {
- let overlay = this.getPluginUI(aPlugin, "main");
- if (overlay) {
- overlay.style.visibility = "hidden";
- }
- },
- stopPlayPreview: function(aPlugin, aPlayPlugin) {
- let objLoadingContent = aPlugin.QueryInterface(Ci.nsIObjectLoadingContent);
- if (objLoadingContent.activated) {
- return;
- }
- if (aPlayPlugin) {
- objLoadingContent.playPlugin();
- } else {
- objLoadingContent.cancelPlayPreview();
- }
- },
- // Callback for user clicking on a disabled plugin
- managePlugins: function(aEvent) {
- BrowserOpenAddonsMgr("addons://list/plugin");
- },
- // Callback for user clicking a "reload page" link
- reloadPage: function(browser) {
- browser.reload();
- },
- // Callback for user clicking the help icon
- openHelpPage: function() {
- openHelpLink("plugin-crashed", false);
- },
- // Event listener for click-to-play plugins.
- _handleClickToPlayEvent: function(aPlugin) {
- let doc = aPlugin.ownerDocument;
- let browser = gBrowser.getBrowserForDocument(doc.defaultView.top.document);
- let pluginHost = Cc["@mozilla.org/plugin/host;1"].getService(Ci.nsIPluginHost);
- let objLoadingContent = aPlugin.QueryInterface(Ci.nsIObjectLoadingContent);
- // guard against giving pluginHost.getPermissionStringForType a type
- // not associated with any known plugin
- if (!gPluginHandler.isKnownPlugin(objLoadingContent)) {
- return;
- }
- let permissionString = pluginHost.getPermissionStringForType(objLoadingContent.actualType);
- let principal = doc.defaultView.top.document.nodePrincipal;
- let pluginPermission = Services.perms.testPermissionFromPrincipal(principal, permissionString);
- let overlay = this.getPluginUI(aPlugin, "main");
- if (pluginPermission == Ci.nsIPermissionManager.DENY_ACTION) {
- if (overlay) {
- overlay.style.visibility = "hidden";
- }
- return;
- }
- if (overlay) {
- overlay.addEventListener("click", gPluginHandler._overlayClickListener, true);
- let closeIcon = gPluginHandler.getPluginUI(aPlugin, "closeIcon");
- closeIcon.addEventListener("click", function(aEvent) {
- if (aEvent.button == 0 && aEvent.isTrusted) {
- gPluginHandler.hideClickToPlayOverlay(aPlugin);
- }
- }, true);
- }
- },
- _overlayClickListener: {
- handleEvent: function(aEvent) {
- let plugin = document.getBindingParent(aEvent.target);
- let contentWindow = plugin.ownerDocument.defaultView.top;
- // gBrowser.getBrowserForDocument does not exist in the case where we
- // drag-and-dropped a tab from a window containing only that tab. In
- // that case, the window gets destroyed.
- let browser = gBrowser.getBrowserForDocument ?
- gBrowser.getBrowserForDocument(contentWindow.document) :
- null;
- // If browser is null here, we've been drag-and-dropped from another
- // window, and this is the wrong click handler.
- if (!browser) {
- aEvent.target.removeEventListener("click", gPluginHandler._overlayClickListener, true);
- return;
- }
- let objLoadingContent = plugin.QueryInterface(Ci.nsIObjectLoadingContent);
- // Have to check that the target is not the link to update the plugin
- if (!(aEvent.originalTarget instanceof HTMLAnchorElement) &&
- (aEvent.originalTarget.getAttribute('anonid') != 'closeIcon') &&
- aEvent.button == 0 && aEvent.isTrusted) {
- gPluginHandler._showClickToPlayNotification(browser, plugin);
- aEvent.stopPropagation();
- aEvent.preventDefault();
- }
- }
- },
- _handlePlayPreviewEvent: function(aPlugin) {
- let doc = aPlugin.ownerDocument;
- let browser = gBrowser.getBrowserForDocument(doc.defaultView.top.document);
- let pluginHost = Cc["@mozilla.org/plugin/host;1"].getService(Ci.nsIPluginHost);
- let pluginInfo = this._getPluginInfo(aPlugin);
- let playPreviewInfo = pluginHost.getPlayPreviewInfo(pluginInfo.mimetype);
- let previewContent = this.getPluginUI(aPlugin, "previewPluginContent");
- let iframe = previewContent.getElementsByClassName("previewPluginContentFrame")[0];
- if (!iframe) {
- // lazy initialization of the iframe
- iframe = doc.createElementNS("http://www.w3.org/1999/xhtml", "iframe");
- iframe.className = "previewPluginContentFrame";
- previewContent.appendChild(iframe);
- // Force a style flush, so that we ensure our binding is attached.
- aPlugin.clientTop;
- }
- iframe.src = playPreviewInfo.redirectURL;
- // MozPlayPlugin event can be dispatched from the extension chrome
- // code to replace the preview content with the native plugin
- previewContent.addEventListener("MozPlayPlugin", function playPluginHandler(aEvent) {
- if (!aEvent.isTrusted) {
- return;
- }
- previewContent.removeEventListener("MozPlayPlugin", playPluginHandler, true);
- let playPlugin = !aEvent.detail;
- gPluginHandler.stopPlayPreview(aPlugin, playPlugin);
- // cleaning up: removes overlay iframe from the DOM
- let iframe = previewContent.getElementsByClassName("previewPluginContentFrame")[0];
- if (iframe) {
- previewContent.removeChild(iframe);
- }
- }, true);
- if (!playPreviewInfo.ignoreCTP) {
- gPluginHandler._showClickToPlayNotification(browser);
- }
- },
- reshowClickToPlayNotification: function() {
- let browser = gBrowser.selectedBrowser;
- let contentWindow = browser.contentWindow;
- let cwu = contentWindow.QueryInterface(Ci.nsIInterfaceRequestor)
- .getInterface(Ci.nsIDOMWindowUtils);
- let doc = contentWindow.document;
- let plugins = cwu.plugins;
- for (let plugin of plugins) {
- let overlay = doc.getAnonymousElementByAttribute(plugin, "anonid", "main");
- if (overlay) {
- overlay.removeEventListener("click", gPluginHandler._overlayClickListener, true);
- }
- let objLoadingContent = plugin.QueryInterface(Ci.nsIObjectLoadingContent);
- if (gPluginHandler.canActivatePlugin(objLoadingContent)) {
- gPluginHandler._handleClickToPlayEvent(plugin);
- }
- }
- gPluginHandler._showClickToPlayNotification(browser);
- },
- _clickToPlayNotificationEventCallback: function(event) {
- if (event == "showing") {
- gPluginHandler._makeCenterActions(this);
- } else if (event == "dismissed") {
- // Once the popup is dismissed, clicking the icon should show the full
- // list again
- this.options.primaryPlugin = null;
- }
- },
- _makeCenterActions: function(notification) {
- let contentWindow = notification.browser.contentWindow;
- let cwu = contentWindow.QueryInterface(Ci.nsIInterfaceRequestor)
- .getInterface(Ci.nsIDOMWindowUtils);
- let principal = contentWindow.document.nodePrincipal;
- let centerActions = [];
- let pluginsFound = new Set();
- for (let plugin of cwu.plugins) {
- plugin.QueryInterface(Ci.nsIObjectLoadingContent);
- if (plugin.getContentTypeForMIMEType(plugin.actualType) != Ci.nsIObjectLoadingContent.TYPE_PLUGIN) {
- continue;
- }
- let pluginInfo = this._getPluginInfo(plugin);
- if (pluginInfo.permissionString === null) {
- Components.utils.reportError("No permission string for active plugin.");
- continue;
- }
- if (pluginsFound.has(pluginInfo.permissionString)) {
- continue;
- }
- pluginsFound.add(pluginInfo.permissionString);
- // Add the per-site permissions and details URLs to pluginInfo here
- // because they are more expensive to compute and so we avoid it in
- // the tighter loop above.
- let permissionObj = Services.perms.
- getPermissionObject(principal, pluginInfo.permissionString, false);
- if (permissionObj) {
- pluginInfo.pluginPermissionPrePath = permissionObj.principal.originNoSuffix;
- pluginInfo.pluginPermissionType = permissionObj.expireType;
- } else {
- pluginInfo.pluginPermissionPrePath = principal.originNoSuffix;
- pluginInfo.pluginPermissionType = undefined;
- }
- let url;
- if (pluginInfo.blocklistState != Ci.nsIBlocklistService.STATE_NOT_BLOCKED) {
- url = Services.blocklist.getPluginBlocklistURL(pluginInfo.pluginTag);
- }
- pluginInfo.detailsLink = url;
- centerActions.push(pluginInfo);
- }
- centerActions.sort(function(a, b) {
- return a.pluginName.localeCompare(b.pluginName);
- });
- notification.options.centerActions = centerActions;
- },
- /**
- * Called from the plugin doorhanger to set the new permissions for a plugin
- * and activate plugins if necessary.
- * aNewState should be either "allownow" "allowalways" or "block"
- */
- _updatePluginPermission: function(aNotification, aPluginInfo, aNewState) {
- let permission;
- let expireType;
- let expireTime;
- switch (aNewState) {
- case "allownow":
- permission = Ci.nsIPermissionManager.ALLOW_ACTION;
- expireType = Ci.nsIPermissionManager.EXPIRE_SESSION;
- expireTime = Date.now() + Services.prefs.getIntPref(kPrefSessionPersistMinutes) * 60 * 1000;
- break;
- case "allowalways":
- permission = Ci.nsIPermissionManager.ALLOW_ACTION;
- expireType = Ci.nsIPermissionManager.EXPIRE_TIME;
- expireTime = Date.now() +
- Services.prefs.getIntPref(kPrefPersistentDays) * 24 * 60 * 60 * 1000;
- break;
- case "block":
- permission = Ci.nsIPermissionManager.PROMPT_ACTION;
- expireType = Ci.nsIPermissionManager.EXPIRE_NEVER;
- expireTime = 0;
- break;
- // In case a plugin has already been allowed in another tab, the "continue allowing" button
- // shouldn't change any permissions but should run the plugin-enablement code below.
- case "continue":
- break;
- default:
- Cu.reportError(Error("Unexpected plugin state: " + aNewState));
- return;
- }
- let browser = aNotification.browser;
- let contentWindow = browser.contentWindow;
- if (aNewState != "continue") {
- let principal = contentWindow.document.nodePrincipal;
- Services.perms.addFromPrincipal(principal, aPluginInfo.permissionString,
- permission, expireType, expireTime);
- if (aNewState == "block") {
- return;
- }
- }
- // Manually activate the plugins that would have been automatically
- // activated.
- let cwu = contentWindow.QueryInterface(Ci.nsIInterfaceRequestor)
- .getInterface(Ci.nsIDOMWindowUtils);
- let plugins = cwu.plugins;
- let pluginHost = Cc["@mozilla.org/plugin/host;1"].getService(Ci.nsIPluginHost);
- for (let plugin of plugins) {
- plugin.QueryInterface(Ci.nsIObjectLoadingContent);
- // canActivatePlugin will return false if this isn't a known plugin type,
- // so the pluginHost.getPermissionStringForType call is protected
- if (gPluginHandler.canActivatePlugin(plugin) &&
- aPluginInfo.permissionString == pluginHost.getPermissionStringForType(plugin.actualType)) {
- plugin.playPlugin();
- }
- }
- },
- _showClickToPlayNotification: function(aBrowser, aPrimaryPlugin) {
- let notification = PopupNotifications.getNotification("click-to-play-plugins", aBrowser);
- let contentWindow = aBrowser.contentWindow;
- let contentDoc = aBrowser.contentDocument;
- let cwu = contentWindow.QueryInterface(Ci.nsIInterfaceRequestor)
- .getInterface(Ci.nsIDOMWindowUtils);
- // Pale Moon: cwu.plugins may contain non-plugin <object>s, filter them out
- let plugins = cwu.plugins.filter(function(plugin) {
- return (plugin.getContentTypeForMIMEType(plugin.actualType) ==
- Ci.nsIObjectLoadingContent.TYPE_PLUGIN);
- });
- if (plugins.length == 0) {
- if (notification) {
- PopupNotifications.remove(notification);
- }
- return;
- }
- let icon = 'plugins-notification-icon';
- for (let plugin of plugins) {
- let fallbackType = plugin.pluginFallbackType;
- if (fallbackType == plugin.PLUGIN_VULNERABLE_UPDATABLE ||
- fallbackType == plugin.PLUGIN_VULNERABLE_NO_UPDATE ||
- fallbackType == plugin.PLUGIN_BLOCKLISTED) {
- icon = 'blocked-plugins-notification-icon';
- break;
- }
- if (fallbackType == plugin.PLUGIN_CLICK_TO_PLAY) {
- let overlay = contentDoc.getAnonymousElementByAttribute(plugin, "anonid", "main");
- if (!overlay || overlay.style.visibility == 'hidden') {
- icon = 'alert-plugins-notification-icon';
- }
- }
- }
- let dismissed = notification ? notification.dismissed : true;
- if (aPrimaryPlugin) {
- dismissed = false;
- }
- let primaryPluginPermission = null;
- if (aPrimaryPlugin) {
- primaryPluginPermission = this._getPluginInfo(aPrimaryPlugin).permissionString;
- }
- let options = {
- dismissed: dismissed,
- eventCallback: this._clickToPlayNotificationEventCallback,
- primaryPlugin: primaryPluginPermission
- };
- PopupNotifications.show(aBrowser, "click-to-play-plugins",
- "", icon, null, null, options);
- },
- // Crashed-plugin observer. Notified once per plugin crash, before events
- // are dispatched to individual plugin instances.
- pluginCrashed : function(subject, topic, data) {
- let propertyBag = subject;
- if (!(propertyBag instanceof Ci.nsIPropertyBag2) ||
- !(propertyBag instanceof Ci.nsIWritablePropertyBag2)) {
- return;
- }
- },
- // Crashed-plugin event listener. Called for every instance of a
- // plugin in content.
- pluginInstanceCrashed: function(plugin, aEvent) {
- // Ensure the plugin and event are of the right type.
- if (!(aEvent instanceof Ci.nsIDOMDataContainerEvent)) {
- return;
- }
- let submittedReport = aEvent.getData("submittedCrashReport");
- let doPrompt = true; // XXX followup for .getData("doPrompt");
- let submitReports = true; // XXX followup for .getData("submitReports");
- let pluginName = aEvent.getData("pluginName");
- let pluginDumpID = aEvent.getData("pluginDumpID");
- let browserDumpID = aEvent.getData("browserDumpID");
- // Remap the plugin name to a more user-presentable form.
- pluginName = this.makeNicePluginName(pluginName);
- let messageString = gNavigatorBundle.getFormattedString("crashedpluginsMessage.title", [pluginName]);
- //
- // Configure the crashed-plugin placeholder.
- //
- // Force a layout flush so the binding is attached.
- plugin.clientTop;
- let doc = plugin.ownerDocument;
- let overlay = doc.getAnonymousElementByAttribute(plugin, "class", "mainBox");
- let statusDiv = doc.getAnonymousElementByAttribute(plugin, "class", "submitStatus");
- let crashText = doc.getAnonymousElementByAttribute(plugin, "class", "msgCrashedText");
- crashText.textContent = messageString;
- let browser = gBrowser.getBrowserForDocument(doc.defaultView.top.document);
- let link = doc.getAnonymousElementByAttribute(plugin, "class", "reloadLink");
- this.addLinkClickCallback(link, "reloadPage", browser);
- let notificationBox = gBrowser.getNotificationBox(browser);
- let isShowing = true;
- // Is the <object>'s size too small to hold what we want to show?
- if (this.isTooSmall(plugin, overlay)) {
- // First try hiding the crash report submission UI.
- statusDiv.removeAttribute("status");
- if (this.isTooSmall(plugin, overlay)) {
- // Hide the overlay's contents. Use visibility style, so that it doesn't
- // collapse down to 0x0.
- overlay.style.visibility = "hidden";
- isShowing = false;
- }
- }
- if (isShowing) {
- // If a previous plugin on the page was too small and resulted in adding a
- // notification bar, then remove it because this plugin instance it big
- // enough to serve as in-content notification.
- hideNotificationBar();
- doc.mozNoPluginCrashedNotification = true;
- } else {
- // If another plugin on the page was large enough to show our UI, we don't
- // want to show a notification bar.
- if (!doc.mozNoPluginCrashedNotification)
- showNotificationBar(pluginDumpID, browserDumpID);
- }
- function hideNotificationBar() {
- let notification = notificationBox.getNotificationWithValue("plugin-crashed");
- if (notification) {
- notificationBox.removeNotification(notification, true);
- }
- }
- function showNotificationBar(pluginDumpID, browserDumpID) {
- // If there's already an existing notification bar, don't do anything.
- let notification = notificationBox.getNotificationWithValue("plugin-crashed");
- if (notification) {
- return;
- }
- // Configure the notification bar
- let priority = notificationBox.PRIORITY_WARNING_MEDIUM;
- let iconURL = "chrome://mozapps/skin/plugins/notifyPluginCrashed.png";
- let reloadLabel = gNavigatorBundle.getString("crashedpluginsMessage.reloadButton.label");
- let reloadKey = gNavigatorBundle.getString("crashedpluginsMessage.reloadButton.accesskey");
- let submitLabel = gNavigatorBundle.getString("crashedpluginsMessage.submitButton.label");
- let submitKey = gNavigatorBundle.getString("crashedpluginsMessage.submitButton.accesskey");
- let buttons = [{
- label: reloadLabel,
- accessKey: reloadKey,
- popup: null,
- callback: function() { browser.reload(); },
- }];
- notification = notificationBox.appendNotification(messageString, "plugin-crashed",
- iconURL, priority, buttons);
- // Add the "learn more" link.
- let XULNS = "http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul";
- let link = notification.ownerDocument.createElementNS(XULNS, "label");
- link.className = "text-link";
- link.setAttribute("value", gNavigatorBundle.getString("crashedpluginsMessage.learnMore"));
- let crashurl = formatURL("app.support.baseURL", true);
- crashurl += "plugin-crashed-notificationbar";
- link.href = crashurl;
- let description = notification.ownerDocument.getAnonymousElementByAttribute(notification, "anonid", "messageText");
- description.appendChild(link);
- // Remove the notfication when the page is reloaded.
- doc.defaultView.top.addEventListener("unload", function() {
- notificationBox.removeNotification(notification);
- }, false);
- }
- }
- };
|