зеркало из https://github.com/mozilla/gecko-dev.git
Bug 745187 - Don't introduce a delay notifying the frontend about new plugins added to the document, because script may immediately remove them from the page. To fix the delayed layout of XBL, introduce a separate method to calculate the notification icon visibility, r=jaws
Note: this patch still has one potential hole: if content creates a plugin and destroys it without forcing reflow, the XBL binding will not be instantiated. This doesn't appear to be a problem in practice, because every site that wants to use a plugin also has to trigger frame construction to get the plugin to instantiate (document.write of the embed element appears to do this automatically).
This commit is contained in:
Родитель
7db9933ab3
Коммит
2ee5dadfec
|
@ -214,6 +214,14 @@ var gPluginHandler = {
|
|||
|
||||
handleEvent : function(event) {
|
||||
let eventType = event.type;
|
||||
|
||||
if (eventType == "PluginRemoved") {
|
||||
let doc = event.target;
|
||||
let browser = gBrowser.getBrowserForDocument(doc.defaultView.top.document);
|
||||
this._setPluginNotificationIcon(browser);
|
||||
return;
|
||||
}
|
||||
|
||||
let plugin = event.target;
|
||||
let doc = plugin.ownerDocument;
|
||||
|
||||
|
@ -313,6 +321,7 @@ var gPluginHandler = {
|
|||
|
||||
plugin.addEventListener("overflow", function(event) {
|
||||
overlay.style.visibility = "hidden";
|
||||
gPluginHandler._setPluginNotificationIcon(browser);
|
||||
});
|
||||
plugin.addEventListener("underflow", function(event) {
|
||||
// this is triggered if only one dimension underflows,
|
||||
|
@ -320,6 +329,7 @@ var gPluginHandler = {
|
|||
if (!gPluginHandler.isTooSmall(plugin, overlay)) {
|
||||
overlay.style.visibility = "visible";
|
||||
}
|
||||
gPluginHandler._setPluginNotificationIcon(browser);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
@ -784,14 +794,6 @@ var gPluginHandler = {
|
|||
continue;
|
||||
}
|
||||
|
||||
// Assume that plugins are hidden and then set override later
|
||||
pluginInfo.hidden = true;
|
||||
|
||||
let overlay = this.getPluginUI(plugin, "main");
|
||||
if (overlay && overlay.style.visibility != "hidden" && overlay.style.visibility != "") {
|
||||
pluginInfo.hidden = false;
|
||||
}
|
||||
|
||||
let permissionObj = Services.perms.
|
||||
getPermissionObject(principal, pluginInfo.permissionString, false);
|
||||
if (permissionObj) {
|
||||
|
@ -819,26 +821,6 @@ var gPluginHandler = {
|
|||
centerActions.set(pluginInfo.permissionString, pluginInfo);
|
||||
}
|
||||
|
||||
let pluginBlocked = false;
|
||||
let pluginHidden = false;
|
||||
for (let pluginInfo of centerActions.values()) {
|
||||
let fallbackType = pluginInfo.fallbackType;
|
||||
if (fallbackType == Ci.nsIObjectLoadingContent.PLUGIN_VULNERABLE_UPDATABLE ||
|
||||
fallbackType == Ci.nsIObjectLoadingContent.PLUGIN_VULNERABLE_NO_UPDATE ||
|
||||
fallbackType == Ci.nsIObjectLoadingContent.PLUGIN_BLOCKLISTED) {
|
||||
pluginBlocked = true;
|
||||
pluginHidden = false;
|
||||
break;
|
||||
}
|
||||
if (fallbackType == Ci.nsIObjectLoadingContent.PLUGIN_CLICK_TO_PLAY && pluginInfo.hidden) {
|
||||
pluginHidden = true;
|
||||
}
|
||||
}
|
||||
|
||||
let iconClasses = document.getElementById("plugins-notification-icon").classList;
|
||||
iconClasses.toggle("plugin-blocked", pluginBlocked);
|
||||
iconClasses.toggle("plugin-hidden", pluginHidden);
|
||||
|
||||
let primaryPluginPermission = null;
|
||||
if (aShowNow) {
|
||||
primaryPluginPermission = this._getPluginInfo(aPlugin).permissionString;
|
||||
|
@ -850,6 +832,7 @@ var gPluginHandler = {
|
|||
if (aShowNow) {
|
||||
notification.options.primaryPlugin = primaryPluginPermission;
|
||||
notification.reshow();
|
||||
setTimeout(() => { this._setPluginNotificationIcon(aBrowser); }, 0);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
@ -863,6 +846,63 @@ var gPluginHandler = {
|
|||
PopupNotifications.show(aBrowser, "click-to-play-plugins",
|
||||
"", "plugins-notification-icon",
|
||||
null, null, options);
|
||||
setTimeout(() => { this._setPluginNotificationIcon(aBrowser); }, 0);
|
||||
},
|
||||
|
||||
_setPluginNotificationIcon : function PH_setPluginNotificationIcon(aBrowser) {
|
||||
// Because this is called on a timeout, sanity-check before continuing
|
||||
if (!aBrowser.docShell || !aBrowser.contentWindow) {
|
||||
return;
|
||||
}
|
||||
|
||||
let notification = PopupNotifications.getNotification("click-to-play-plugins", aBrowser);
|
||||
if (!notification)
|
||||
return;
|
||||
|
||||
let iconClasses = document.getElementById("plugins-notification-icon").classList;
|
||||
|
||||
// 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;
|
||||
}
|
||||
}
|
||||
|
||||
iconClasses.remove("plugin-blocked");
|
||||
|
||||
let contentWindow = aBrowser.contentWindow;
|
||||
let contentDoc = aBrowser.contentDocument;
|
||||
let cwu = contentWindow.QueryInterface(Ci.nsIInterfaceRequestor)
|
||||
.getInterface(Ci.nsIDOMWindowUtils);
|
||||
for (let plugin of cwu.plugins) {
|
||||
let fallbackType = plugin.pluginFallbackType;
|
||||
if (fallbackType != Ci.nsIObjectLoadingContent.PLUGIN_CLICK_TO_PLAY) {
|
||||
continue;
|
||||
}
|
||||
let info = this._getPluginInfo(plugin);
|
||||
if (!actions.has(info.permissionString)) {
|
||||
continue;
|
||||
}
|
||||
let overlay = this.getPluginUI(plugin, "main");
|
||||
if (!overlay) {
|
||||
continue;
|
||||
}
|
||||
if (!this.isTooSmall(plugin, overlay)) {
|
||||
actions.delete(info.permissionString);
|
||||
if (actions.size == 0) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
iconClasses.toggle("plugin-hidden", actions.size != 0);
|
||||
},
|
||||
|
||||
// Crashed-plugin observer. Notified once per plugin crash, before events
|
||||
|
|
|
@ -756,6 +756,7 @@ var gBrowserInit = {
|
|||
gBrowser.addEventListener("PluginCrashed", gPluginHandler, true);
|
||||
gBrowser.addEventListener("PluginOutdated", gPluginHandler, true);
|
||||
gBrowser.addEventListener("PluginInstantiated", gPluginHandler, true);
|
||||
gBrowser.addEventListener("PluginRemoved", gPluginHandler, true);
|
||||
|
||||
gBrowser.addEventListener("NewPluginInstalled", gPluginHandler.newPluginInstalled, true);
|
||||
|
||||
|
|
|
@ -61,6 +61,7 @@ support-files =
|
|||
plugin_data_url.html
|
||||
plugin_hidden_to_visible.html
|
||||
plugin_small.html
|
||||
plugin_syncRemoved.html
|
||||
plugin_test.html
|
||||
plugin_test2.html
|
||||
plugin_test3.html
|
||||
|
|
|
@ -835,6 +835,14 @@ function test24d() {
|
|||
function() {
|
||||
clearAllPluginPermissions();
|
||||
resetBlocklist();
|
||||
finishTest();
|
||||
prepareTest(test25, gTestRoot + "plugin_syncRemoved.html");
|
||||
});
|
||||
}
|
||||
|
||||
function test25() {
|
||||
let notification = PopupNotifications.getNotification("click-to-play-plugins");
|
||||
ok(notification, "Test 25: There should be a plugin notification even if the plugin was immediately removed");
|
||||
ok(notification.dismissed, "Test 25: The notification should be dismissed by default");
|
||||
|
||||
finishTest();
|
||||
}
|
||||
|
|
|
@ -0,0 +1,14 @@
|
|||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<body>
|
||||
<script type="text/javascript">
|
||||
// create an embed, insert it in the doc and immediately remove it
|
||||
var embed = document.createElement('embed');
|
||||
embed.setAttribute("type", "application/x-test");
|
||||
embed.setAttribute("style", "width: 0px; height: 0px;");
|
||||
document.body.appendChild(embed);
|
||||
window.getComputedStyle(embed, null).top;
|
||||
document.body.remove(embed);
|
||||
</script>
|
|
@ -80,7 +80,7 @@
|
|||
// Notify browser-plugins.js that we were attached, on a delay because
|
||||
// this binding doesn't complete layout until the constructor
|
||||
// completes.
|
||||
setTimeout(() => { this.dispatchEvent(new CustomEvent("PluginBindingAttached")) }, 0);
|
||||
this.dispatchEvent(new CustomEvent("PluginBindingAttached"));
|
||||
</constructor>
|
||||
</implementation>
|
||||
</binding>
|
||||
|
|
Загрузка…
Ссылка в новой задаче