Bug 932854 - when a site uses a hidden plugin, show a notification bar to aid discoverability. This patch, suitable for trunk and branch, uses existing strings which are not ideal. r=jaws

This commit is contained in:
Benjamin Smedberg 2013-11-11 16:11:12 -05:00
parent e7c483c950
commit 9e19f125a5
4 changed files with 152 additions and 23 deletions

View File

@ -880,24 +880,26 @@ var gPluginHandler = {
if (!notification)
return;
let iconClasses = document.getElementById("plugins-notification-icon").classList;
// Make a copy of the actions, removing active plugins and checking for
// outdated plugins.
let haveInsecure = false;
let actions = new Map();
for (let action of notification.options.centerActions.values()) {
switch (action.fallbackType) {
// haveInsecure will trigger the red flashing icon and the infobar
// styling below
case Ci.nsIObjectLoadingContent.PLUGIN_VULNERABLE_UPDATABLE:
case Ci.nsIObjectLoadingContent.PLUGIN_VULNERABLE_NO_UPDATE:
haveInsecure = true;
// fall through
// Make a copy so we can remove visible plugins
let actions = new Map(notification.options.centerActions);
for (let pluginInfo of actions.values()) {
let fallbackType = pluginInfo.fallbackType;
if (fallbackType == Ci.nsIObjectLoadingContent.PLUGIN_VULNERABLE_UPDATABLE ||
fallbackType == Ci.nsIObjectLoadingContent.PLUGIN_VULNERABLE_NO_UPDATE ||
fallbackType == Ci.nsIObjectLoadingContent.PLUGIN_BLOCKLISTED) {
iconClasses.add("plugin-blocked");
iconClasses.remove("plugin-hidden");
return;
case Ci.nsIObjectLoadingContent.PLUGIN_CLICK_TO_PLAY:
actions.set(action.permissionString, action);
continue;
}
}
iconClasses.remove("plugin-blocked");
// check for hidden plugins
let contentWindow = aBrowser.contentWindow;
let contentDoc = aBrowser.contentDocument;
let cwu = contentWindow.QueryInterface(Ci.nsIInterfaceRequestor)
@ -915,7 +917,9 @@ var gPluginHandler = {
}
continue;
}
if (fallbackType != Ci.nsIObjectLoadingContent.PLUGIN_CLICK_TO_PLAY) {
if (fallbackType != Ci.nsIObjectLoadingContent.PLUGIN_CLICK_TO_PLAY &&
fallbackType != Ci.nsIObjectLoadingContent.PLUGIN_VULNERABLE_UPDATABLE &&
fallbackType != Ci.nsIObjectLoadingContent.PLUGIN_VULNERABLE_NO_UPDATE) {
continue;
}
let overlay = this.getPluginUI(plugin, "main");
@ -930,7 +934,116 @@ var gPluginHandler = {
}
}
iconClasses.toggle("plugin-hidden", actions.size != 0);
// Set up the icon
document.getElementById("plugins-notification-icon").classList.
toggle("plugin-blocked", haveInsecure);
// Now configure the notification bar
let notificationBox = gBrowser.getNotificationBox(aBrowser);
function hideNotification() {
let n = notificationBox.getNotificationWithValue("plugin-hidden");
if (n) {
notificationBox.removeNotification(n, true);
}
}
// There are three different cases when showing an infobar:
// 1. A single type of plugin is hidden on the page. Show the UI for that
// plugin.
// 2a. Multiple types of plugins are hidden on the page. Show the multi-UI
// with the vulnerable styling.
// 2b. Multiple types of plugins are hidden on the page, but none are
// vulnerable. Show the nonvulnerable multi-UI.
function showNotification() {
let n = notificationBox.getNotificationWithValue("plugin-hidden");
if (n) {
// If something is already shown, just keep it
return;
}
let message;
// Icons set directly cannot be manipulated using moz-image-region, so
// we use CSS classes instead.
let host = gPluginHandler._getHostFromPrincipal(aBrowser.contentDocument.nodePrincipal);
let brand = document.getElementById("bundle_brand").getString("brandShortName");
if (actions.size == 1) {
let pluginInfo = [...actions.values()][0];
let pluginName = pluginInfo.pluginName;
switch (pluginInfo.fallbackType) {
case Ci.nsIObjectLoadingContent.PLUGIN_CLICK_TO_PLAY:
message = gNavigatorBundle.getFormattedString(
"pluginActivateNew.message",
[pluginName, host]);
break;
case Ci.nsIObjectLoadingContent.PLUGIN_VULNERABLE_UPDATABLE:
message = gNavigatorBundle.getFormattedString(
"pluginActivateOutdated.message",
[pluginName, host, brand]);
break;
case Ci.nsIObjectLoadingContent.PLUGIN_VULNERABLE_NO_UPDATE:
message = gNavigatorBundle.getFormattedString(
"pluginActivateVulnerable.message",
[pluginName, host, brand]);
}
} else {
// Multi-plugin
message = gNavigatorBundle.getFormattedString(
"pluginActivateMultiple.message", [host]);
for (let action of actions.values()) {
if (action.fallbackType != Ci.nsIObjectLoadingContent.PLUGIN_CLICK_TO_PLAY) {
break;
}
}
}
// These strings are temporary no-string-change for branch uplift
let buttons = [
{
label: gNavigatorBundle.getString("pluginBlockNow.label"),
accessKey: gNavigatorBundle.getString("pluginBlockNow.accesskey"),
callback: function() {
Services.perms.addFromPrincipal(aBrowser.contentDocument.nodePrincipal,
"plugin-hidden-notification",
Services.perms.DENY_ACTION);
}
},
{
label: gNavigatorBundle.getString("offlineApps.allow"),
accessKey: gNavigatorBundle.getString("offlineApps.allowAccessKey"),
callback: function() {
let curNotification =
PopupNotifications.getNotification("click-to-play-plugins",
aBrowser);
if (curNotification) {
curNotification.reshow();
}
}
}
];
n = notificationBox.
appendNotification(message, "plugin-hidden", null,
notificationBox.PRIORITY_INFO_HIGH, buttons);
if (haveInsecure) {
n.classList.add('pluginVulnerable');
}
}
if (actions.size == 0) {
hideNotification();
} else {
let notificationPermission = Services.perms.testPermissionFromPrincipal(
aBrowser.contentDocument.nodePrincipal, "plugin-hidden-notification");
if (notificationPermission == Ci.nsIPermissionManager.DENY_ACTION) {
hideNotification();
} else {
showNotification();
}
}
},
// Crashed-plugin observer. Notified once per plugin crash, before events

View File

@ -851,9 +851,9 @@ function test26() {
let notification = PopupNotifications.getNotification("click-to-play-plugins");
ok(notification, "Test 26: There should be a plugin notification");
waitForCondition(() => gBrowser.ownerDocument.
getElementById("plugins-notification-icon").classList.
contains("plugin-hidden"),
let notificationBox = gBrowser.getNotificationBox(gTestBrowser);
waitForCondition(() => notificationBox.getNotificationWithValue("plugin-hidden") !== null,
() => {
// Don't use setTestPluginEnabledState here because we already saved the
// prior value
@ -867,9 +867,9 @@ function test27() {
let notification = PopupNotifications.getNotification("click-to-play-plugins");
ok(notification, "Test 27: There should be a plugin notification");
waitForCondition(() => !gBrowser.ownerDocument.
getElementById("plugins-notification-icon").classList.
contains("plugin-hidden"),
let notificationBox = gBrowser.getNotificationBox(gTestBrowser);
waitForCondition(() => notificationBox.getNotificationWithValue("plugin-hidden") === null,
finishTest,
"Test 27, expected the plugin notification icon to not be highlighted");
}

View File

@ -47,3 +47,19 @@
.center-item-link {
margin: 0;
}
.messageImage[value="plugin-hidden"] {
list-style-image: url("chrome://browser/skin/notification-pluginNormal.png");
-moz-image-region: rect(0, 16px, 16px, 0);
}
/* Keep any changes to this style in sync with pluginProblem.css */
notification.pluginVulnerable {
background-color: rgb(72,72,72);
background-image: url(chrome://mozapps/skin/plugins/contentPluginStripe.png);
color: white;
}
notification.pluginVulnerable .messageImage {
list-style-image: url("chrome://browser/skin/notification-pluginBlocked.png");
}

View File

@ -5,7 +5,7 @@
@namespace html url(http://www.w3.org/1999/xhtml);
/* These styles affect only the bound element, not other page content. */
/* Keep any changes to these styles in sync with plugin-doorhanger.inc.css */
.mainBox {
font: message-box;
font-size: 12px;