Bug 1398972 - Replace usage of plugin doorhanger XBL binding r?felipe
Migrated to simply use PopupNotifications.jsm. Additionally, this
changes the behavior to always have two buttons and a remember
checkbox. When selecting allow with remember, it will behave like
the always allow option previously, but when selecting block with
remember, it will move that page into a quiet mode with respect
to Flash - i.e., no plugin overlays will show anymore, and instead
you will just see the plugin icon in the URL bar, which you can
continue to interact with as before.
MozReview-Commit-ID: EUFlI7nM09t
--- a/browser/base/content/browser-plugins.js
+++ b/browser/base/content/browser-plugins.js
@@ -137,84 +137,103 @@ var gPluginHandler = {
}
},
/**
* 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(aNotification, aPluginInfo, aNewState) {
+ _updatePluginPermission(aBrowser, aPluginInfo, aNewState) {
let permission;
let expireType;
let expireTime;
let histogram =
Services.telemetry.getHistogramById("PLUGINS_NOTIFICATION_USER_ACTION");
+ let notification = PopupNotifications.getNotification("click-to-play-plugins", aBrowser);
+
// Update the permission manager.
// Also update the current state of pluginInfo.fallbackType so that
// subsequent opening of the notification shows the current state.
switch (aNewState) {
case "allownow":
permission = Ci.nsIPermissionManager.ALLOW_ACTION;
expireType = Ci.nsIPermissionManager.EXPIRE_SESSION;
expireTime = Date.now() + Services.prefs.getIntPref(this.PREF_SESSION_PERSIST_MINUTES) * 60 * 1000;
histogram.add(0);
aPluginInfo.fallbackType = Ci.nsIObjectLoadingContent.PLUGIN_ACTIVE;
- aNotification.options.extraAttr = "active";
+ notification.options.extraAttr = "active";
break;
case "allowalways":
permission = Ci.nsIPermissionManager.ALLOW_ACTION;
expireType = Ci.nsIPermissionManager.EXPIRE_TIME;
expireTime = Date.now() +
Services.prefs.getIntPref(this.PREF_PERSISTENT_DAYS) * 24 * 60 * 60 * 1000;
histogram.add(1);
aPluginInfo.fallbackType = Ci.nsIObjectLoadingContent.PLUGIN_ACTIVE;
- aNotification.options.extraAttr = "active";
+ notification.options.extraAttr = "active";
break;
case "block":
permission = Ci.nsIPermissionManager.PROMPT_ACTION;
expireType = Ci.nsIPermissionManager.EXPIRE_NEVER;
expireTime = 0;
histogram.add(2);
switch (aPluginInfo.blocklistState) {
case Ci.nsIBlocklistService.STATE_VULNERABLE_UPDATE_AVAILABLE:
aPluginInfo.fallbackType = Ci.nsIObjectLoadingContent.PLUGIN_VULNERABLE_UPDATABLE;
break;
case Ci.nsIBlocklistService.STATE_VULNERABLE_NO_UPDATE:
aPluginInfo.fallbackType = Ci.nsIObjectLoadingContent.PLUGIN_VULNERABLE_NO_UPDATE;
break;
default:
- aPluginInfo.fallbackType = Ci.nsIObjectLoadingContent.PLUGIN_CLICK_TO_PLAY;
+ // PLUGIN_CLICK_TO_PLAY_QUIET will only last until they reload the page, at
+ // which point it will be PLUGIN_CLICK_TO_PLAY (the overlays will appear)
+ aPluginInfo.fallbackType = Ci.nsIObjectLoadingContent.PLUGIN_CLICK_TO_PLAY_QUIET;
}
- aNotification.options.extraAttr = "inactive";
+ notification.options.extraAttr = "inactive";
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 "blockalways":
+ permission = Ci.nsIObjectLoadingContent.PLUGIN_PERMISSION_PROMPT_ACTION_QUIET;
+ expireType = Ci.nsIPermissionManager.EXPIRE_NEVER;
+ expireTime = 0;
+ histogram.add(3);
+ aPluginInfo.fallbackType = Ci.nsIObjectLoadingContent.PLUGIN_CLICK_TO_PLAY_QUIET;
+ notification.options.extraAttr = "inactive";
+ break;
+
+ // In case a plugin has already been allowed/disallowed in another tab, the
+ // buttons matching the existing block state shouldn't change any permissions
+ // but should run the plugin-enablement code below.
case "continue":
aPluginInfo.fallbackType = Ci.nsIObjectLoadingContent.PLUGIN_ACTIVE;
- aNotification.options.extraAttr = "active";
+ notification.options.extraAttr = "active";
break;
+
+ case "continueblocking":
+ aPluginInfo.fallbackType = Ci.nsIObjectLoadingContent.PLUGIN_CLICK_TO_PLAY_QUIET;
+ notification.options.extraAttr = "inactive";
+ break;
+
default:
Cu.reportError(Error("Unexpected plugin state: " + aNewState));
return;
}
- let browser = aNotification.browser;
- if (aNewState != "continue") {
- let principal = aNotification.options.principal;
+ if (aNewState != "continue" && aNewState != "continueblocking") {
+ let principal = notification.options.principal;
Services.perms.addFromPrincipal(principal, aPluginInfo.permissionString,
permission, expireType, expireTime);
aPluginInfo.pluginPermissionType = expireType;
}
- browser.messageManager.sendAsyncMessage("BrowserPlugins:ActivatePlugins", {
+ aBrowser.messageManager.sendAsyncMessage("BrowserPlugins:ActivatePlugins", {
pluginInfo: aPluginInfo,
newState: aNewState,
});
},
showClickToPlayNotification(browser, plugins, showNow,
principal, location) {
// It is possible that we've received a message from the frame script to show
@@ -240,26 +259,21 @@ var gPluginHandler = {
// If this is a new notification, create a pluginData map, otherwise append
let pluginData;
if (notification) {
pluginData = notification.options.pluginData;
} else {
pluginData = new Map();
}
- let hasInactivePlugins = true;
- for (var pluginInfo of plugins) {
+ for (let pluginInfo of plugins) {
if (pluginData.has(pluginInfo.permissionString)) {
continue;
}
- if (pluginInfo.fallbackType == Ci.nsIObjectLoadingContent.PLUGIN_ACTIVE) {
- hasInactivePlugins = false;
- }
-
// If a block contains an infoURL, we should always prefer that to the default
// URL that we construct in-product, even for other blocklist types.
let url = Services.blocklist.getPluginInfoURL(pluginInfo.pluginTag);
if (pluginInfo.blocklistState != Ci.nsIBlocklistService.STATE_NOT_BLOCKED) {
if (!url) {
url = Services.blocklist.getPluginBlocklistURL(pluginInfo.pluginTag);
}
@@ -282,40 +296,115 @@ var gPluginHandler = {
if (showNow) {
notification.options.primaryPlugin = primaryPluginPermission;
notification.reshow();
browser.messageManager.sendAsyncMessage("BrowserPlugins:NotificationShown");
}
return;
}
- let options = {
- dismissed: !showNow,
- persistent: showNow,
- eventCallback: this._clickToPlayNotificationEventCallback,
- primaryPlugin: primaryPluginPermission,
- pluginData,
- principal,
- extraAttr: hasInactivePlugins ? "inactive" : "active",
- };
+ if (plugins.length == 1) {
+ let pluginInfo = plugins[0];
+ // If a block contains an infoURL, we should always prefer that to the default
+ // URL that we construct in-product, even for other blocklist types.
+ let url = Services.blocklist.getPluginInfoURL(pluginInfo.pluginTag);
+
+ if (pluginInfo.blocklistState != Ci.nsIBlocklistService.STATE_NOT_BLOCKED) {
+ if (!url) {
+ url = Services.blocklist.getPluginBlocklistURL(pluginInfo.pluginTag);
+ }
+ } else {
+ url = Services.urlFormatter.formatURLPref("app.support.baseURL") + "clicktoplay";
+ }
+ pluginInfo.detailsLink = url;
+
+ let chromeWin = window.QueryInterface(Ci.nsIDOMChromeWindow);
+ let isWindowPrivate = PrivateBrowsingUtils.isWindowPrivate(chromeWin);
+
+ let active = pluginInfo.fallbackType == Ci.nsIObjectLoadingContent.PLUGIN_ACTIVE;
+
+ let options = {
+ dismissed: !showNow,
+ hideClose: !Services.prefs.getBoolPref("privacy.permissionPrompts.showCloseButton"),
+ persistent: showNow,
+ eventCallback: this._clickToPlayNotificationEventCallback,
+ primaryPlugin: primaryPluginPermission,
+ popupIconClass: "plugin-icon",
+ extraAttr: active ? "active" : "inactive",
+ pluginData,
+ principal,
+ };
+
+ let description;
+ if (pluginInfo.fallbackType == Ci.nsIObjectLoadingContent.PLUGIN_VULNERABLE_UPDATABLE) {
+ description = gNavigatorBundle.getString("flashActivate.outdated.message");
+ } else {
+ description = gNavigatorBundle.getString("flashActivate.message");
+ }
+
+ let badge = document.getElementById("plugin-icon-badge");
+ badge.setAttribute("animate", "true");
+ badge.addEventListener("animationend", function animListener(event) {
+ if (event.animationName == "blink-badge" &&
+ badge.hasAttribute("animate")) {
+ badge.removeAttribute("animate");
+ badge.removeEventListener("animationend", animListener);
+ }
+ });
- let badge = document.getElementById("plugin-icon-badge");
- badge.setAttribute("animate", "true");
- badge.addEventListener("animationend", function animListener(event) {
- if (event.animationName == "blink-badge" &&
- badge.hasAttribute("animate")) {
- badge.removeAttribute("animate");
- badge.removeEventListener("animationend", animListener);
+ let weakBrowser = Cu.getWeakReference(browser);
+ let mainAction = {
+ callback: ({checkboxChecked}) => {
+ let browserRef = weakBrowser.get();
+ if (browserRef) {
+ if (checkboxChecked) {
+ this._updatePluginPermission(browserRef, pluginInfo, "allowalways");
+ } else if (pluginInfo.fallbackType == Ci.nsIObjectLoadingContent.PLUGIN_ACTIVE) {
+ this._updatePluginPermission(browserRef, pluginInfo, "continue");
+ } else {
+ this._updatePluginPermission(browserRef, pluginInfo, "allownow");
+ }
+ }
+ },
+ label: gNavigatorBundle.getString("flashActivate.allow"),
+ accessKey: gNavigatorBundle.getString("flashActivate.allow.accesskey"),
+ dismiss: true,
+ };
+
+ let secondaryActions = null;
+ if (!isWindowPrivate) {
+ options.checkbox = {
+ label: gNavigatorBundle.getString("flashActivate.remember"),
+ };
+ secondaryActions = [{
+ callback: ({checkboxChecked}) => {
+ let browserRef = weakBrowser.get();
+ if (browserRef) {
+ if (checkboxChecked) {
+ this._updatePluginPermission(browserRef, pluginInfo, "blockalways");
+ } else if (pluginInfo.fallbackType == Ci.nsIObjectLoadingContent.PLUGIN_ACTIVE) {
+ this._updatePluginPermission(browserRef, pluginInfo, "block");
+ } else {
+ this._updatePluginPermission(browserRef, pluginInfo, "continueblocking");
+ }
+ }
+ },
+ label: gNavigatorBundle.getString("flashActivate.noAllow"),
+ accessKey: gNavigatorBundle.getString("flashActivate.noAllow.accesskey"),
+ dismiss: true,
+ }];
}
- });
- PopupNotifications.show(browser, "click-to-play-plugins",
- "", "plugins-notification-icon",
- null, null, options);
- browser.messageManager.sendAsyncMessage("BrowserPlugins:NotificationShown");
+ PopupNotifications.show(browser, "click-to-play-plugins",
+ description, "plugins-notification-icon",
+ mainAction, secondaryActions, options);
+ browser.messageManager.sendAsyncMessage("BrowserPlugins:NotificationShown");
+ } else {
+ this.removeNotification(browser, "click-to-play-plugins");
+ }
},
removeNotification(browser, name) {
let notification = PopupNotifications.getNotification(name, browser);
if (notification)
PopupNotifications.remove(notification);
},
@@ -405,16 +494,17 @@ var gPluginHandler = {
let brand = document.getElementById("bundle_brand").getString("brandShortName");
if (actions.length == 1) {
let pluginInfo = actions[0];
let pluginName = pluginInfo.pluginName;
switch (pluginInfo.fallbackType) {
case Ci.nsIObjectLoadingContent.PLUGIN_CLICK_TO_PLAY:
+ case Ci.nsIObjectLoadingContent.PLUGIN_CLICK_TO_PLAY_QUIET:
message = gNavigatorBundle.getFormattedString(
"pluginActivationWarning.message",
[brand]);
break;
case Ci.nsIObjectLoadingContent.PLUGIN_VULNERABLE_UPDATABLE:
message = gNavigatorBundle.getFormattedString(
"pluginActivateOutdated.message",
[pluginName, origin, brand]);
--- a/browser/base/content/browser.css
+++ b/browser/base/content/browser.css
@@ -997,25 +997,16 @@ html|*#fullscreen-exit-button {
pointer-events: none;
-moz-stack-sizing: ignore;
}
#addon-progress-notification {
-moz-binding: url("chrome://browser/content/urlbarBindings.xml#addon-progress-notification");
}
-#click-to-play-plugins-notification {
- -moz-binding: url("chrome://browser/content/urlbarBindings.xml#click-to-play-plugins-notification");
-}
-
-
-.plugin-popupnotification-centeritem {
- -moz-binding: url("chrome://browser/content/urlbarBindings.xml#plugin-popupnotification-center-item");
-}
-
browser[tabmodalPromptShowing] {
-moz-user-focus: none !important;
}
/* Status panel */
statuspanel {
-moz-binding: url("chrome://browser/content/tabbrowser.xml#statuspanel");
--- a/browser/locales/en-US/chrome/browser/browser.properties
+++ b/browser/locales/en-US/chrome/browser/browser.properties
@@ -340,16 +340,25 @@ pluginActivateNow.accesskey=N
# plugin.
pluginActivateAlways.label=Allow and Remember
pluginActivateAlways.accesskey=R
pluginBlockNow.label=Block Plugin
pluginBlockNow.accesskey=B
pluginContinue.label=Continue Allowing
pluginContinue.accesskey=C
+# Flash activation doorhanger UI
+flashActivate.message=Do you want to allow Adobe Flash to run on this site? Only allow Adobe Flash on sites you trust.
+flashActivate.outdated.message=Do you want to allow an outdated version of Adobe Flash to run on this site? An outdated version can affect browser performance and security.
+flashActivate.remember=Remember this decision
+flashActivate.noAllow=Don’t Allow
+flashActivate.allow=Allow
+flashActivate.noAllow.accesskey=D
+flashActivate.allow.accesskey=A
+
# in-page UI
PluginClickToActivate=Activate %S.
PluginVulnerableUpdatable=This plugin is vulnerable and should be updated.
PluginVulnerableNoUpdate=This plugin has security vulnerabilities.
# infobar UI
pluginContinueBlocking.label=Continue Blocking
pluginContinueBlocking.accesskey=B
--- a/browser/modules/PluginContent.jsm
+++ b/browser/modules/PluginContent.jsm
@@ -25,16 +25,20 @@ XPCOMUtils.defineLazyModuleGetter(this,
this.PluginContent = function(global) {
this.init(global);
};
const FLASH_MIME_TYPE = "application/x-shockwave-flash";
const REPLACEMENT_STYLE_SHEET = Services.io.newURI("chrome://pluginproblem/content/pluginReplaceBinding.css");
+const OVERLAY_DISPLAY_HIDDEN = 0;
+const OVERLAY_DISPLAY_VISIBLE = 1;
+const OVERLAY_DISPLAY_MINIMAL = 2;
+
PluginContent.prototype = {
init(global) {
this.global = global;
// Need to hold onto the content window or else it'll get destroyed
this.content = this.global.content;
// Cache of plugin actions for the current page.
this.pluginData = new Map();
// Cache of plugin crash information sent from the parent
@@ -279,46 +283,55 @@ PluginContent.prototype = {
fallbackType: Ci.nsIObjectLoadingContent.PLUGIN_CLICK_TO_PLAY,
blocklistState,
};
},
/**
* Update the visibility of the plugin overlay.
*/
- setVisibility(plugin, overlay, shouldShow) {
- overlay.classList.toggle("visible", shouldShow);
- if (shouldShow) {
+ setVisibility(plugin, overlay, overlayDisplayState) {
+ overlay.classList.toggle("visible", overlayDisplayState != OVERLAY_DISPLAY_HIDDEN);
+ overlay.classList.toggle("minimal", overlayDisplayState == OVERLAY_DISPLAY_MINIMAL)
+ if (overlayDisplayState == OVERLAY_DISPLAY_VISIBLE) {
overlay.removeAttribute("dismissed");
}
},
/**
* Check whether the plugin should be visible on the page. A plugin should
* not be visible if the overlay is too big, or if any other page content
* overlays it.
*
* This function will handle showing or hiding the overlay.
* @returns true if the plugin is invisible.
*/
- shouldShowOverlay(plugin, overlay) {
+ computeOverlayDisplayState(plugin, overlay) {
+ let fallbackType = plugin.pluginFallbackType;
+ if (plugin.pluginFallbackTypeOverride !== undefined) {
+ fallbackType = plugin.pluginFallbackTypeOverride;
+ }
+ if (fallbackType == Ci.nsIObjectLoadingContent.PLUGIN_CLICK_TO_PLAY_QUIET) {
+ return OVERLAY_DISPLAY_HIDDEN;
+ }
+
// If the overlay size is 0, we haven't done layout yet. Presume that
// plugins are visible until we know otherwise.
if (overlay.scrollWidth == 0) {
- return true;
+ return OVERLAY_DISPLAY_VISIBLE;
}
// 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.
let overflows = (overlay.scrollWidth > Math.ceil(pluginRect.width)) ||
(overlay.scrollHeight - 5 > Math.ceil(pluginRect.height));
if (overflows) {
- return false;
+ return OVERLAY_DISPLAY_MINIMAL;
}
// Is the plugin covered up by other content so that it is not clickable?
// Floating point can confuse .elementFromPoint, so inset just a bit
let left = pluginRect.left + 2;
let right = pluginRect.right - 2;
let top = pluginRect.top + 2;
let bottom = pluginRect.bottom - 2;
@@ -335,21 +348,21 @@ PluginContent.prototype = {
.getInterface(Ci.nsIDOMWindowUtils);
for (let [x, y] of points) {
if (x < 0 || y < 0) {
continue;
}
let el = cwu.elementFromPoint(x, y, true, true);
if (el === plugin) {
- return true;
+ return OVERLAY_DISPLAY_VISIBLE;
}
}
- return false;
+ return OVERLAY_DISPLAY_HIDDEN;
},
addLinkClickCallback(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) {
@@ -387,16 +400,17 @@ PluginContent.prototype = {
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:
+ case Ci.nsIObjectLoadingContent.PLUGIN_CLICK_TO_PLAY_QUIET:
return "PluginClickToPlay";
case Ci.nsIObjectLoadingContent.PLUGIN_VULNERABLE_UPDATABLE:
return "PluginVulnerableUpdatable";
case Ci.nsIObjectLoadingContent.PLUGIN_VULNERABLE_NO_UPDATE:
return "PluginVulnerableNoUpdate";
default:
// Not all states map to a handler
return null;
@@ -457,17 +471,17 @@ PluginContent.prototype = {
}
}
let plugin = event.target;
if (eventType == "PluginPlaceholderReplaced") {
plugin.removeAttribute("href");
let overlay = this.getPluginUI(plugin, "main");
- this.setVisibility(plugin, overlay, true);
+ this.setVisibility(plugin, overlay, OVERLAY_DISPLAY_VISIBLE);
let inIDOMUtils = Cc["@mozilla.org/inspector/dom-utils;1"]
.getService(Ci.inIDOMUtils);
// Add psuedo class so our styling will take effect
inIDOMUtils.addPseudoClassLock(plugin, "-moz-handler-clicktoplay");
overlay.addEventListener("click", this, true);
return;
}
@@ -542,20 +556,20 @@ PluginContent.prototype = {
break;
}
// Show the in-content UI if it's not too big. The crashed plugin handler already did this.
let overlay = this.getPluginUI(plugin, "main");
if (eventType != "PluginCrashed") {
if (overlay != null) {
this.setVisibility(plugin, overlay,
- this.shouldShowOverlay(plugin, overlay));
+ this.computeOverlayDisplayState(plugin, overlay));
let resizeListener = () => {
this.setVisibility(plugin, overlay,
- this.shouldShowOverlay(plugin, overlay));
+ this.computeOverlayDisplayState(plugin, overlay));
this.updateNotificationUI();
};
plugin.addEventListener("overflow", resizeListener);
plugin.addEventListener("underflow", resizeListener);
}
}
let closeIcon = this.getPluginUI(plugin, "closeIcon");
@@ -587,17 +601,17 @@ PluginContent.prototype = {
let pluginHost = Cc["@mozilla.org/plugin/host;1"].getService(Ci.nsIPluginHost);
let permissionString = pluginHost.getPermissionStringForType(objLoadingContent.actualType);
let principal = objLoadingContent.ownerGlobal.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;
+ objLoadingContent.pluginFallbackType <= Ci.nsIObjectLoadingContent.PLUGIN_CLICK_TO_PLAY_QUIET;
return !objLoadingContent.activated &&
pluginPermission != Ci.nsIPermissionManager.DENY_ACTION &&
isFallbackTypeValid;
},
hideClickToPlayOverlay(plugin) {
let overlay = this.getPluginUI(plugin, "main");
@@ -660,17 +674,18 @@ PluginContent.prototype = {
permissionString = pluginHost.getPermissionStringForType(objLoadingContent.actualType);
}
let principal = doc.defaultView.top.document.nodePrincipal;
let pluginPermission = Services.perms.testPermissionFromPrincipal(principal, permissionString);
let overlay = this.getPluginUI(plugin, "main");
- if (pluginPermission == Ci.nsIPermissionManager.DENY_ACTION) {
+ if (pluginPermission == Ci.nsIPermissionManager.DENY_ACTION ||
+ pluginPermission == Ci.nsIObjectLoadingContent.PLUGIN_PERMISSION_PROMPT_ACTION_QUIET) {
if (overlay) {
overlay.classList.remove("visible");
}
return;
}
if (overlay) {
overlay.addEventListener("click", this, true);
@@ -728,35 +743,36 @@ PluginContent.prototype = {
}
if (pluginInfo.permissionString == pluginHost.getPermissionStringForType(plugin.actualType)) {
let overlay = this.getPluginUI(plugin, "main");
if (ChromeUtils.getClassName(plugin) === "HTMLAnchorElement") {
placeHolderFound = true;
} else {
pluginFound = true;
}
- if (newState == "block") {
+ if (newState == "block" || newState == "blockalways" || newState == "continueblocking") {
if (overlay) {
overlay.addEventListener("click", this, true);
}
+ plugin.pluginFallbackTypeOverride = pluginInfo.fallbackType;
plugin.reload(true);
} else if (this.canActivatePlugin(plugin)) {
if (overlay) {
overlay.removeEventListener("click", this, true);
}
plugin.playPlugin();
}
}
}
// If there are no instances of the plugin on the page any more, what the
// user probably needs is for us to allow and then refresh. Additionally, if
// this is content that requires HLS or we replaced the placeholder the page
// needs to be refreshed for it to insert its plugins
- if (newState != "block" &&
+ if (newState != "block" && newState != "blockalways" && newState != "continueblocking" &&
(!pluginFound || placeHolderFound || contentWindow.pluginRequiresReload)) {
this.reloadPage();
}
this.updateNotificationUI();
},
_showClickToPlayNotification(plugin, showNow) {
let plugins = [];
@@ -849,16 +865,17 @@ PluginContent.prototype = {
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
+ case Ci.nsIObjectLoadingContent.PLUGIN_CLICK_TO_PLAY_QUIET:
case Ci.nsIObjectLoadingContent.PLUGIN_CLICK_TO_PLAY:
actions.set(action.permissionString, action);
continue;
}
}
// Remove plugins that are already active, or large enough to show an overlay.
let cwu = this.content.QueryInterface(Ci.nsIInterfaceRequestor)
@@ -872,27 +889,28 @@ PluginContent.prototype = {
if (fallbackType == Ci.nsIObjectLoadingContent.PLUGIN_ACTIVE) {
actions.delete(info.permissionString);
if (actions.size == 0) {
break;
}
continue;
}
if (fallbackType != Ci.nsIObjectLoadingContent.PLUGIN_CLICK_TO_PLAY &&
+ fallbackType != Ci.nsIObjectLoadingContent.PLUGIN_CLICK_TO_PLAY_QUIET &&
fallbackType != Ci.nsIObjectLoadingContent.PLUGIN_VULNERABLE_UPDATABLE &&
fallbackType != Ci.nsIObjectLoadingContent.PLUGIN_VULNERABLE_NO_UPDATE) {
continue;
}
let overlay = this.getPluginUI(plugin, "main");
if (!overlay) {
continue;
}
- let shouldShow = this.shouldShowOverlay(plugin, overlay);
- this.setVisibility(plugin, overlay, shouldShow);
- if (shouldShow) {
+ let overlayDisplayState = this.computeOverlayDisplayState(plugin, overlay);
+ this.setVisibility(plugin, overlay, overlayDisplayState);
+ if (overlayDisplayState == OVERLAY_DISPLAY_VISIBLE) {
actions.delete(info.permissionString);
if (actions.size == 0) {
break;
}
}
}
// If there are any items remaining in `actions` now, they are hidden
@@ -1066,31 +1084,31 @@ PluginContent.prototype = {
this.addLinkClickCallback(helpIcon, "openHelpPage");
let crashText = this.getPluginUI(plugin, "crashedText");
crashText.textContent = message;
let link = this.getPluginUI(plugin, "reloadLink");
this.addLinkClickCallback(link, "reloadPage");
- let isShowing = this.shouldShowOverlay(plugin, overlay);
+ let overlayDisplayState = this.computeOverlayDisplayState(plugin, overlay);
// Is the <object>'s size too small to hold what we want to show?
- if (!isShowing) {
+ if (overlayDisplayState != OVERLAY_DISPLAY_VISIBLE) {
// First try hiding the crash report submission UI.
statusDiv.removeAttribute("status");
- isShowing = this.shouldShowOverlay(plugin, overlay);
+ overlayDisplayState = this.computeOverlayDisplayState(plugin, overlay);
}
- this.setVisibility(plugin, overlay, isShowing);
+ this.setVisibility(plugin, overlay, overlayDisplayState);
let doc = plugin.ownerDocument;
let runID = plugin.runID;
- if (isShowing) {
+ if (overlayDisplayState == OVERLAY_DISPLAY_VISIBLE) {
// 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.
this.hideNotificationBar("plugin-crashed");
doc.mozNoPluginCrashedNotification = true;
// Notify others that the crash reporter UI is now ready.
// Currently, this event is only used by tests.
--- a/dom/base/nsIObjectLoadingContent.idl
+++ b/dom/base/nsIObjectLoadingContent.idl
@@ -54,23 +54,32 @@ interface nsIObjectLoadingContent : nsIS
const unsigned long PLUGIN_CRASHED = 5;
// Suppressed by security policy
const unsigned long PLUGIN_SUPPRESSED = 6;
// Blocked by content policy
const unsigned long PLUGIN_USER_DISABLED = 7;
/// ** All values >= PLUGIN_CLICK_TO_PLAY are plugin placeholder types that
/// would be replaced by a real plugin if activated (playPlugin())
/// ** Furthermore, values >= PLUGIN_CLICK_TO_PLAY and
- /// <= PLUGIN_VULNERABLE_NO_UPDATE are click-to-play types.
+ /// <= PLUGIN_CLICK_TO_PLAY_QUIET are click-to-play types.
// The plugin is disabled until the user clicks on it
const unsigned long PLUGIN_CLICK_TO_PLAY = 8;
// The plugin is vulnerable (update available)
const unsigned long PLUGIN_VULNERABLE_UPDATABLE = 9;
// The plugin is vulnerable (no update available)
const unsigned long PLUGIN_VULNERABLE_NO_UPDATE = 10;
+ // The plugin is click-to-play, but the user won't see overlays
+ const unsigned long PLUGIN_CLICK_TO_PLAY_QUIET = 11;
+
+ // Plugins-specific permission indicating that we want to prompt the user
+ // to decide whether they want to allow a plugin, but to do so in a less
+ // intrusive way than PROMPT_ACTION would entail. At the time of writing,
+ // this means hiding all in-content plugin overlays, but still showing the
+ // plugin badge in the URL bar.
+ const unsigned long PLUGIN_PERMISSION_PROMPT_ACTION_QUIET = 8;
/**
* The actual mime type (the one we got back from the network
* request) for the element.
*/
readonly attribute ACString actualType;
/**
--- a/dom/base/nsObjectLoadingContent.cpp
+++ b/dom/base/nsObjectLoadingContent.cpp
@@ -1328,16 +1328,17 @@ nsObjectLoadingContent::ObjectState() co
return EventStates();
case eType_Null:
switch (mFallbackType) {
case eFallbackSuppressed:
return NS_EVENT_STATE_SUPPRESSED;
case eFallbackUserDisabled:
return NS_EVENT_STATE_USERDISABLED;
case eFallbackClickToPlay:
+ case eFallbackClickToPlayQuiet:
return NS_EVENT_STATE_TYPE_CLICK_TO_PLAY;
case eFallbackDisabled:
return NS_EVENT_STATE_BROKEN | NS_EVENT_STATE_HANDLER_DISABLED;
case eFallbackBlocklisted:
return NS_EVENT_STATE_BROKEN | NS_EVENT_STATE_HANDLER_BLOCKED;
case eFallbackCrashed:
return NS_EVENT_STATE_BROKEN | NS_EVENT_STATE_HANDLER_CRASHED;
case eFallbackUnsupported:
@@ -2373,17 +2374,17 @@ nsObjectLoadingContent::LoadObject(bool
// If we didn't load anything, handle switching to fallback state
if (mType == eType_Null) {
LOG(("OBJLC [%p]: Loading fallback, type %u", this, fallbackType));
NS_ASSERTION(!mFrameLoader && !mInstanceOwner,
"switched to type null but also loaded something");
// Don't fire error events if we're falling back to click-to-play; instead
// pretend like this is a really slow-loading plug-in instead.
- if (fallbackType != eFallbackClickToPlay) {
+ if (fallbackType != eFallbackClickToPlay && fallbackType != eFallbackClickToPlayQuiet) {
MaybeFireErrorEvent();
}
if (mChannel) {
// If we were loading with a channel but then failed over, throw it away
CloseChannel();
}
@@ -3338,16 +3339,24 @@ nsObjectLoadingContent::ShouldPlay(Fallb
aReason = eFallbackAlternate;
return false;
}
return true;
case nsIPermissionManager::DENY_ACTION:
aReason = eFallbackDisabled;
return false;
+ case PLUGIN_PERMISSION_PROMPT_ACTION_QUIET:
+ if (PreferFallback(true /* isPluginClickToPlay */)) {
+ aReason = eFallbackAlternate;
+ } else {
+ aReason = eFallbackClickToPlayQuiet;
+ }
+
+ return false;
case nsIPermissionManager::PROMPT_ACTION:
if (PreferFallback(true /* isPluginClickToPlay */)) {
// False is already returned in this case, but
// it's important to correctly set aReason too.
aReason = eFallbackAlternate;
}
return false;
--- a/dom/base/nsObjectLoadingContent.h
+++ b/dom/base/nsObjectLoadingContent.h
@@ -89,23 +89,25 @@ class nsObjectLoadingContent : public ns
eFallbackCrashed = nsIObjectLoadingContent::PLUGIN_CRASHED,
// Suppressed by security policy
eFallbackSuppressed = nsIObjectLoadingContent::PLUGIN_SUPPRESSED,
// Blocked by content policy
eFallbackUserDisabled = nsIObjectLoadingContent::PLUGIN_USER_DISABLED,
/// ** All values >= eFallbackClickToPlay are plugin placeholder types
/// that would be replaced by a real plugin if activated (PlayPlugin())
/// ** Furthermore, values >= eFallbackClickToPlay and
- /// <= eFallbackVulnerableNoUpdate are click-to-play types.
+ /// <= eFallbackClickToPlayQuiet are click-to-play types.
// The plugin is disabled until the user clicks on it
eFallbackClickToPlay = nsIObjectLoadingContent::PLUGIN_CLICK_TO_PLAY,
// The plugin is vulnerable (update available)
eFallbackVulnerableUpdatable = nsIObjectLoadingContent::PLUGIN_VULNERABLE_UPDATABLE,
// The plugin is vulnerable (no update available)
eFallbackVulnerableNoUpdate = nsIObjectLoadingContent::PLUGIN_VULNERABLE_NO_UPDATE,
+ // The plugin is click-to-play, but the user won't see overlays
+ eFallbackClickToPlayQuiet = nsIObjectLoadingContent::PLUGIN_CLICK_TO_PLAY_QUIET,
};
nsObjectLoadingContent();
virtual ~nsObjectLoadingContent();
NS_DECL_NSIREQUESTOBSERVER
NS_DECL_NSISTREAMLISTENER
NS_DECL_NSIFRAMELOADEROWNER
--- a/toolkit/pluginproblem/content/pluginProblemContent.css
+++ b/toolkit/pluginproblem/content/pluginProblemContent.css
@@ -44,30 +44,40 @@ a .mainBox:focus,
/* used to block inherited properties */
text-transform: none;
text-indent: 0;
cursor: initial;
white-space: initial;
word-spacing: initial;
letter-spacing: initial;
line-height: initial;
+ visibility: hidden;
+}
+
+.visible {
+ visibility: visible;
}
/* Initialize the overlay with visibility:hidden to prevent flickering if
* the plugin is too small to show the overlay */
.mainBox > .hoverBox,
.mainBox > .closeIcon {
visibility: hidden;
}
.visible > .hoverBox,
.visible > .closeIcon {
visibility: visible;
}
+.minimal > .hoverBox,
+.minimal > .closeIcon {
+ visibility: hidden;
+}
+
.mainBox[chromedir="rtl"] {
direction: rtl;
}
a .hoverBox,
:-moz-handler-clicktoplay .hoverBox,
:-moz-handler-vulnerable-updatable .hoverBox,
:-moz-handler-vulnerable-no-update .hoverBox {