bug 746374 - differentiate click-to-play plugin permissions by type and vulnerability status r=jaws r=joshmoz

This commit is contained in:
David Keeler 2012-11-27 10:09:10 -08:00
parent ae9ea81cff
commit b737ede83b
5 changed files with 258 additions and 18 deletions

View File

@ -238,7 +238,16 @@ var gPluginHandler = {
},
canActivatePlugin: function PH_canActivatePlugin(objLoadingContent) {
let pluginHost = Cc["@mozilla.org/plugin/host;1"].getService(Ci.nsIPluginHost);
let pluginPermission = Ci.nsIPermissionManager.UNKNOWN_ACTION;
if (objLoadingContent.actualType) {
let permissionString = pluginHost.getPermissionStringForType(objLoadingContent.actualType);
let browser = gBrowser.getBrowserForDocument(objLoadingContent.ownerDocument.defaultView.top.document);
pluginPermission = Services.perms.testPermission(browser.currentURI, permissionString);
}
return !objLoadingContent.activated &&
pluginPermission != Ci.nsIPermissionManager.DENY_ACTION &&
objLoadingContent.pluginFallbackType !== Ci.nsIObjectLoadingContent.PLUGIN_PLAY_PREVIEW;
},
@ -351,19 +360,26 @@ var gPluginHandler = {
_handleClickToPlayEvent: function PH_handleClickToPlayEvent(aPlugin) {
let doc = aPlugin.ownerDocument;
let browser = gBrowser.getBrowserForDocument(doc.defaultView.top.document);
let pluginsPermission = Services.perms.testPermission(browser.currentURI, "plugins");
let pluginHost = Cc["@mozilla.org/plugin/host;1"].getService(Ci.nsIPluginHost);
let pluginPermission = Ci.nsIPermissionManager.UNKNOWN_ACTION;
let objLoadingContent = aPlugin.QueryInterface(Ci.nsIObjectLoadingContent);
if (objLoadingContent.actualType) {
let permissionString = pluginHost.getPermissionStringForType(objLoadingContent.actualType);
pluginPermission = Services.perms.testPermission(browser.currentURI, permissionString);
}
let overlay = doc.getAnonymousElementByAttribute(aPlugin, "class", "mainBox");
if (browser._clickToPlayPluginsActivated) {
let objLoadingContent = aPlugin.QueryInterface(Ci.nsIObjectLoadingContent);
objLoadingContent.playPlugin();
return;
} else if (pluginsPermission == Ci.nsIPermissionManager.DENY_ACTION) {
if (pluginPermission == Ci.nsIPermissionManager.DENY_ACTION) {
if (overlay)
overlay.style.visibility = "hidden";
return;
}
if (browser._clickToPlayPluginsActivated) {
objLoadingContent.playPlugin();
return;
}
if (overlay) {
overlay.addEventListener("click", function(aEvent) {
// Have to check that the target is not the link to update the plugin
@ -417,10 +433,6 @@ var gPluginHandler = {
reshowClickToPlayNotification: function PH_reshowClickToPlayNotification() {
let browser = gBrowser.selectedBrowser;
let pluginsPermission = Services.perms.testPermission(browser.currentURI, "plugins");
if (pluginsPermission == Ci.nsIPermissionManager.DENY_ACTION)
return;
if (gPluginHandler._pluginNeedsActivationExceptThese([]))
gPluginHandler._showClickToPlayNotification(browser);
},
@ -513,7 +525,19 @@ var gPluginHandler = {
}
return centerActions;
},
},
_setPermissionForPlugins: function PH_setPermissionForPlugins(aBrowser, aPermission, aPluginList) {
let pluginHost = Cc["@mozilla.org/plugin/host;1"].getService(Ci.nsIPluginHost);
for (let plugin of aPluginList) {
let objLoadingContent = plugin.QueryInterface(Ci.nsIObjectLoadingContent);
if (gPluginHandler.canActivatePlugin(objLoadingContent) &&
objLoadingContent.actualType) {
let permissionString = pluginHost.getPermissionStringForType(objLoadingContent.actualType);
Services.perms.add(aBrowser.currentURI, permissionString, aPermission);
}
}
},
_showClickToPlayNotification: function PH_showClickToPlayNotification(aBrowser) {
aBrowser._clickToPlayDoorhangerShown = true;
@ -541,14 +565,14 @@ var gPluginHandler = {
label: gNavigatorBundle.getString("activatePluginsMessage.always"),
accessKey: gNavigatorBundle.getString("activatePluginsMessage.always.accesskey"),
callback: function () {
Services.perms.add(aBrowser.currentURI, "plugins", Ci.nsIPermissionManager.ALLOW_ACTION);
gPluginHandler._setPermissionForPlugins(aBrowser, Ci.nsIPermissionManager.ALLOW_ACTION, cwu.plugins);
gPluginHandler.activatePlugins(contentWindow);
}
},{
label: gNavigatorBundle.getString("activatePluginsMessage.never"),
accessKey: gNavigatorBundle.getString("activatePluginsMessage.never.accesskey"),
callback: function () {
Services.perms.add(aBrowser.currentURI, "plugins", Ci.nsIPermissionManager.DENY_ACTION);
gPluginHandler._setPermissionForPlugins(aBrowser, Ci.nsIPermissionManager.DENY_ACTION, cwu.plugins);
let notification = PopupNotifications.getNotification("click-to-play-plugins", aBrowser);
if (notification)
notification.remove();
@ -567,7 +591,9 @@ var gPluginHandler = {
.getInterface(Ci.nsIDOMWindowUtils);
for (let plugin of cwu.plugins) {
let overlay = doc.getAnonymousElementByAttribute(plugin, "class", "mainBox");
overlay.style.visibility = "hidden";
// for already activated plugins, there will be no overlay
if (overlay)
overlay.style.visibility = "hidden";
}
},

View File

@ -402,9 +402,26 @@ function test12c() {
var objLoadingContent = plugin.QueryInterface(Ci.nsIObjectLoadingContent);
ok(objLoadingContent.activated, "Test 12c, Plugin should be activated");
prepareTest(test12d, gHttpTestRoot + "plugin_two_types.html");
}
// Test that the "Always" permission, when set for just the Test plugin,
// does not also allow the Second Test plugin.
function test12d() {
var popupNotification = PopupNotifications.getNotification("click-to-play-plugins", gTestBrowser);
ok(popupNotification, "Test 12d, Should have a click-to-play notification");
var test = gTestBrowser.contentDocument.getElementById("test");
var secondtestA = gTestBrowser.contentDocument.getElementById("secondtestA");
var secondtestB = gTestBrowser.contentDocument.getElementById("secondtestB");
var objLoadingContent = test.QueryInterface(Ci.nsIObjectLoadingContent);
ok(objLoadingContent.activated, "Test 12d, Test plugin should be activated");
var objLoadingContent = secondtestA.QueryInterface(Ci.nsIObjectLoadingContent);
ok(!objLoadingContent.activated, "Test 12d, Second Test plugin (A) should not be activated");
var objLoadingContent = secondtestB.QueryInterface(Ci.nsIObjectLoadingContent);
ok(!objLoadingContent.activated, "Test 12d, Second Test plugin (B) should not be activated");
Services.perms.removeAll();
gNextTest = test13a;
gTestBrowser.reload();
prepareTest(test13a, gHttpTestRoot + "plugin_clickToPlayDeny.html");
}
// Tests that the "Deny Always" permission works for click-to-play plugins (part 1/3)
@ -444,6 +461,55 @@ function test13c() {
var overlay = gTestBrowser.contentDocument.getAnonymousElementByAttribute(plugin, "class", "mainBox");
ok(overlay.style.visibility == "hidden", "Test 13c, Plugin should not have visible overlay");
prepareTest(test13d, gHttpTestRoot + "plugin_two_types.html");
}
// Test that the "Deny Always" permission, when set for just the Test plugin,
// does not also block the Second Test plugin (i.e. it gets an overlay and
// there's a notification and everything).
function test13d() {
var popupNotification = PopupNotifications.getNotification("click-to-play-plugins", gTestBrowser);
ok(popupNotification, "Test 13d, Should have a click-to-play notification");
var test = gTestBrowser.contentDocument.getElementById("test");
var objLoadingContent = test.QueryInterface(Ci.nsIObjectLoadingContent);
var overlay = gTestBrowser.contentDocument.getAnonymousElementByAttribute(test, "class", "mainBox");
ok(overlay.style.visibility == "hidden", "Test 13d, Test plugin should not have visible overlay");
ok(!objLoadingContent.activated, "Test 13d, Test plugin should not be activated");
var secondtestA = gTestBrowser.contentDocument.getElementById("secondtestA");
var objLoadingContent = secondtestA.QueryInterface(Ci.nsIObjectLoadingContent);
var overlay = gTestBrowser.contentDocument.getAnonymousElementByAttribute(secondtestA, "class", "mainBox");
ok(overlay.style.visibility != "hidden", "Test 13d, Test plugin should have visible overlay");
ok(!objLoadingContent.activated, "Test 13d, Second Test plugin (A) should not be activated");
var secondtestB = gTestBrowser.contentDocument.getElementById("secondtestB");
var objLoadingContent = secondtestB.QueryInterface(Ci.nsIObjectLoadingContent);
var overlay = gTestBrowser.contentDocument.getAnonymousElementByAttribute(secondtestB, "class", "mainBox");
ok(overlay.style.visibility != "hidden", "Test 13d, Test plugin should have visible overlay");
ok(!objLoadingContent.activated, "Test 13d, Second Test plugin (B) should not be activated");
var condition = function() objLoadingContent.activated;
// "click" "Activate All Plugins"
popupNotification.mainAction.callback();
waitForCondition(condition, test13e, "Test 13d, Waited too long for plugin to activate");
}
// Test that clicking "Activate All Plugins" won't activate plugins that
// have previously been "Deny Always"-ed.
function test13e() {
var test = gTestBrowser.contentDocument.getElementById("test");
var objLoadingContent = test.QueryInterface(Ci.nsIObjectLoadingContent);
ok(!objLoadingContent.activated, "Test 13e, Test plugin should not be activated");
var secondtestA = gTestBrowser.contentDocument.getElementById("secondtestA");
var objLoadingContent = secondtestA.QueryInterface(Ci.nsIObjectLoadingContent);
ok(objLoadingContent.activated, "Test 13e, Second Test plugin (A) should be activated");
var secondtestB = gTestBrowser.contentDocument.getElementById("secondtestB");
var objLoadingContent = secondtestB.QueryInterface(Ci.nsIObjectLoadingContent);
ok(objLoadingContent.activated, "Test 13e, Second Test plugin (B) should be activated");
Services.perms.removeAll();
Services.prefs.setBoolPref("plugins.click_to_play", false);
prepareTest(test14, gTestRoot + "plugin_test2.html");
@ -932,5 +998,123 @@ function test23() {
is(objLoadingContent.pluginFallbackType, Ci.nsIObjectLoadingContent.PLUGIN_CLICK_TO_PLAY, "Test 23, Plugin should be click-to-play");
ok(!pluginNode.activated, "Test 23, plugin node should not be activated");
prepareTest(test24a, gHttpTestRoot + "plugin_test.html");
}
// Test that "always allow"-ing a plugin will not allow it when it becomes
// blocklisted.
function test24a() {
var notification = PopupNotifications.getNotification("click-to-play-plugins", gTestBrowser);
ok(notification, "Test 24a, Should have a click-to-play notification");
var plugin = gTestBrowser.contentDocument.getElementById("test");
ok(plugin, "Test 24a, Found plugin in page");
var objLoadingContent = plugin.QueryInterface(Ci.nsIObjectLoadingContent);
is(objLoadingContent.pluginFallbackType, Ci.nsIObjectLoadingContent.PLUGIN_CLICK_TO_PLAY, "Test 24a, Plugin should be click-to-play");
ok(!objLoadingContent.activated, "Test 24a, plugin should not be activated");
// simulate "always allow"
notification.secondaryActions[0].callback();
prepareTest(test24b, gHttpTestRoot + "plugin_test.html");
}
// did the "always allow" work as intended?
function test24b() {
var notification = PopupNotifications.getNotification("click-to-play-plugins", gTestBrowser);
ok(!notification, "Test 24b, Should not have a click-to-play notification");
var plugin = gTestBrowser.contentDocument.getElementById("test");
ok(plugin, "Test 24b, Found plugin in page");
var objLoadingContent = plugin.QueryInterface(Ci.nsIObjectLoadingContent);
ok(objLoadingContent.activated, "Test 24b, plugin should be activated");
setAndUpdateBlocklist(gHttpTestRoot + "blockPluginVulnerableUpdatable.xml",
function() {
prepareTest(test24c, gHttpTestRoot + "plugin_test.html");
});
}
// the plugin is now blocklisted, so it should not automatically load
function test24c() {
var notification = PopupNotifications.getNotification("click-to-play-plugins", gTestBrowser);
ok(notification, "Test 24c, Should have a click-to-play notification");
var plugin = gTestBrowser.contentDocument.getElementById("test");
ok(plugin, "Test 24c, Found plugin in page");
var objLoadingContent = plugin.QueryInterface(Ci.nsIObjectLoadingContent);
is(objLoadingContent.pluginFallbackType, Ci.nsIObjectLoadingContent.PLUGIN_VULNERABLE_UPDATABLE, "Test 24c, Plugin should be vulnerable/updatable");
ok(!objLoadingContent.activated, "Test 24c, plugin should not be activated");
// simulate "always allow"
notification.secondaryActions[0].callback();
prepareTest(test24d, gHttpTestRoot + "plugin_test.html");
}
// We should still be able to always allow a plugin after we've seen that it's
// blocklisted.
function test24d() {
var notification = PopupNotifications.getNotification("click-to-play-plugins", gTestBrowser);
ok(!notification, "Test 24d, Should not have a click-to-play notification");
var plugin = gTestBrowser.contentDocument.getElementById("test");
ok(plugin, "Test 24d, Found plugin in page");
var objLoadingContent = plugin.QueryInterface(Ci.nsIObjectLoadingContent);
ok(objLoadingContent.activated, "Test 24d, plugin should be activated");
Services.perms.removeAll();
resetBlocklist(function () {
prepareTest(test25a, gHttpTestRoot + "plugin_test.html");
});
}
// Test that clicking "always allow" or "always deny" doesn't affect plugins
// that already have permission given to them
function test25a() {
var notification = PopupNotifications.getNotification("click-to-play-plugins", gTestBrowser);
ok(notification, "Test 25a, Should have a click-to-play notification");
var plugin = gTestBrowser.contentDocument.getElementById("test");
ok(plugin, "Test 25a, Found plugin in page");
var objLoadingContent = plugin.QueryInterface(Ci.nsIObjectLoadingContent);
ok(!objLoadingContent.activated, "Test 25a, plugin should not be activated");
// simulate "always allow"
notification.secondaryActions[0].callback();
prepareTest(test25b, gHttpTestRoot + "plugin_two_types.html");
}
function test25b() {
var notification = PopupNotifications.getNotification("click-to-play-plugins", gTestBrowser);
ok(notification, "Test 25b, Should have a click-to-play notification");
var test = gTestBrowser.contentDocument.getElementById("test");
ok(test, "Test 25b, Found test plugin in page");
var objLoadingContent = test.QueryInterface(Ci.nsIObjectLoadingContent);
ok(objLoadingContent.activated, "Test 25b, test plugin should be activated");
var secondtest = gTestBrowser.contentDocument.getElementById("secondtestA");
ok(secondtest, "Test 25b, Found second test plugin in page");
var objLoadingContent = secondtest.QueryInterface(Ci.nsIObjectLoadingContent);
ok(!objLoadingContent.activated, "Test 25b, second test plugin should not be activated");
// simulate "always deny"
notification.secondaryActions[1].callback();
prepareTest(test25c, gHttpTestRoot + "plugin_two_types.html");
}
// we should have one plugin allowed to activate and the other plugin(s) denied
// (so it should have an invisible overlay)
function test25c() {
var notification = PopupNotifications.getNotification("click-to-play-plugins", gTestBrowser);
ok(!notification, "Test 25c, Should not have a click-to-play notification");
var test = gTestBrowser.contentDocument.getElementById("test");
ok(test, "Test 25c, Found test plugin in page");
var objLoadingContent = test.QueryInterface(Ci.nsIObjectLoadingContent);
ok(objLoadingContent.activated, "Test 25c, test plugin should be activated");
var secondtest = gTestBrowser.contentDocument.getElementById("secondtestA");
ok(secondtest, "Test 25c, Found second test plugin in page");
var objLoadingContent = secondtest.QueryInterface(Ci.nsIObjectLoadingContent);
ok(!objLoadingContent.activated, "Test 25c, second test plugin should not be activated");
var overlay = gTestBrowser.contentDocument.getAnonymousElementByAttribute(secondtest, "class", "mainBox");
ok(overlay.style.visibility == "hidden", "Test 25c, second test plugin should not have visible overlay");
Services.perms.removeAll();
finishTest();
}

View File

@ -2561,9 +2561,12 @@ nsObjectLoadingContent::ShouldPlay(FallbackType &aReason)
// the system principal, i.e. in chrome pages. That way the click-to-play
// code here wouldn't matter at all. Bug 775301 is tracking this.
if (!nsContentUtils::IsSystemPrincipal(topDoc->NodePrincipal())) {
nsAutoCString permissionString;
rv = pluginHost->GetPermissionStringForType(mContentType, permissionString);
NS_ENSURE_SUCCESS(rv, false);
uint32_t permission;
rv = permissionManager->TestPermissionFromPrincipal(topDoc->NodePrincipal(),
"plugins",
permissionString.Data(),
&permission);
NS_ENSURE_SUCCESS(rv, false);
allowPerm = permission == nsIPermissionManager::ALLOW_ACTION;

View File

@ -12,7 +12,7 @@
"@mozilla.org/plugin/host;1"
%}
[scriptable, uuid(d70af999-cb1f-4429-b85e-f18cdbabc43c)]
[scriptable, uuid(3ac8fe33-c38c-4123-b2f0-0e8a2824b9c5)]
interface nsIPluginHost : nsISupports
{
/**
@ -82,6 +82,8 @@ interface nsIPluginHost : nsISupports
void unregisterPlayPreviewMimeType(in AUTF8String mimeType);
ACString getPermissionStringForType(in AUTF8String mimeType);
bool isPluginClickToPlayForType(in AUTF8String mimeType);
};

View File

@ -1347,6 +1347,31 @@ nsPluginHost::GetBlocklistStateForType(const char *aMimeType, uint32_t *aState)
return NS_ERROR_FAILURE;
}
NS_IMETHODIMP
nsPluginHost::GetPermissionStringForType(const nsACString &aMimeType, nsACString &aPermissionString)
{
aPermissionString.Truncate();
uint32_t blocklistState;
nsresult rv = GetBlocklistStateForType(aMimeType.Data(), &blocklistState);
NS_ENSURE_SUCCESS(rv, rv);
nsPluginTag *tag = FindPluginForType(aMimeType.Data(), true);
if (!tag) {
return NS_ERROR_FAILURE;
}
if (blocklistState == nsIBlocklistService::STATE_VULNERABLE_UPDATE_AVAILABLE ||
blocklistState == nsIBlocklistService::STATE_VULNERABLE_NO_UPDATE) {
aPermissionString.AssignLiteral("plugin-vulnerable:");
}
else {
aPermissionString.AssignLiteral("plugin:");
}
aPermissionString.Append(tag->mFileName);
return NS_OK;
}
// check comma delimitered extensions
static int CompareExtensions(const char *aExtensionList, const char *aExtension)
{