bug 760625 - use the blocklist to inform click-to-play plugins. r=joshmoz,bmcbride

This commit is contained in:
David Keeler 2012-07-11 08:56:34 -07:00
parent 665908a65f
commit 679f55960f
23 changed files with 454 additions and 72 deletions

View File

@ -140,6 +140,12 @@ var gPluginHandler = {
self.pluginUnavailable(plugin, event.type);
break;
case "PluginVulnerableUpdatable":
let updateLink = doc.getAnonymousElementByAttribute(plugin, "class", "checkForUpdatesLink");
self.addLinkClickCallback(updateLink, "openPluginUpdatePage");
/* FALLTHRU */
case "PluginVulnerableNoUpdate":
case "PluginClickToPlay":
self._handleClickToPlayEvent(plugin);
break;
@ -151,9 +157,10 @@ var gPluginHandler = {
}
// Hide the in-content UI if it's too big. The crashed plugin handler already did this.
if (event.type != "PluginCrashed" && event.type != "PluginClickToPlay") {
if (event.type != "PluginCrashed") {
let overlay = doc.getAnonymousElementByAttribute(plugin, "class", "mainBox");
if (self.isTooSmall(plugin, overlay))
/* overlay might be null, so only operate on it if it exists */
if (overlay != null && self.isTooSmall(plugin, overlay))
overlay.style.visibility = "hidden";
}
},
@ -224,6 +231,12 @@ var gPluginHandler = {
managePlugins: function (aEvent) {
BrowserOpenAddonsMgr("addons://list/plugin");
},
// Callback for user clicking on the link in a click-to-play plugin
// (where the plugin has an update)
openPluginUpdatePage: function (aEvent) {
openURL(Services.urlFormatter.formatURLPref("plugins.update.url"));
},
#ifdef MOZ_CRASHREPORTER
// Callback for user clicking "submit a report" link
@ -258,15 +271,18 @@ var gPluginHandler = {
objLoadingContent.playPlugin();
return;
} else if (pluginsPermission == Ci.nsIPermissionManager.DENY_ACTION) {
overlay.style.visibility = "hidden";
if (overlay)
overlay.style.visibility = "hidden";
return;
}
let overlay = doc.getAnonymousElementByAttribute(aPlugin, "class", "mainBox");
// The overlay is null if the XBL binding is not attached (element is display:none).
if (overlay) {
overlay.addEventListener("click", function(aEvent) {
if (aEvent.button == 0 && aEvent.isTrusted)
// Have to check that the target is a XULElement and not the link
// to update the plugin
if (aEvent.target instanceof XULElement &&
aEvent.button == 0 && aEvent.isTrusted)
gPluginHandler.activateSinglePlugin(aEvent.target.ownerDocument.defaultView.top, aPlugin);
}, true);
}

View File

@ -1001,6 +1001,8 @@ var gBrowserInit = {
gBrowser.addEventListener("PluginOutdated", gPluginHandler, true);
gBrowser.addEventListener("PluginDisabled", gPluginHandler, true);
gBrowser.addEventListener("PluginClickToPlay", gPluginHandler, true);
gBrowser.addEventListener("PluginVulnerableUpdatable", gPluginHandler, true);
gBrowser.addEventListener("PluginVulnerableNoUpdate", gPluginHandler, true);
gBrowser.addEventListener("NewPluginInstalled", gPluginHandler.newPluginInstalled, true);
#ifdef XP_MACOSX
gBrowser.addEventListener("npapi-carbon-event-model-failure", gPluginHandler, true);

View File

@ -517,5 +517,103 @@ function test17() {
var missingNotification = PopupNotifications.getNotification("missing-plugins", gTestBrowser);
ok(!missingNotification, "Test 17, Should not have a missing plugin notification");
registerFakeBlocklistService(Ci.nsIBlocklistService.STATE_VULNERABLE_UPDATE_AVAILABLE);
prepareTest(test18a, gTestRoot + "plugin_test.html");
}
const Cr = Components.results;
const Cm = Components.manager;
const Cc = Components.classes;
const gReg = Cm.QueryInterface(Ci.nsIComponentRegistrar);
const gRealBlocklistServiceCID = Cc["@mozilla.org/extensions/blocklist;1"];
const gFakeBlocklistServiceCID = Components.ID("{614b68a0-3c53-4ec0-8146-28cc1e25f8a1}");
var gFactory = null;
function registerFakeBlocklistService(blockState) {
var BlocklistService = {
getPluginBlocklistState: function(plugin, appVersion, toolkitVersion) {
return blockState;
},
classID: gFakeBlocklistServiceCID,
QueryInterface: XPCOMUtils.generateQI([Ci.nsIBlocklistService])
};
gFactory = {
createInstance: function(outer, iid) {
if (outer != null)
throw Cr.NS_ERROR_NO_AGGREGATION;
return BlocklistService.QueryInterface(iid);
}
};
gReg.registerFactory(gFakeBlocklistServiceCID,
"Fake Blocklist Service",
"@mozilla.org/extensions/blocklist;1",
gFactory);
}
function unregisterFakeBlocklistService() {
if (gFactory != null ) {
gReg.unregisterFactory(gFakeBlocklistServiceCID, gFactory);
gFactory = null;
// This should restore the original blocklist service:
gReg.registerFactory(gRealBlocklistServiceCID,
"Blocklist Service",
"@mozilla.org/extensions/blocklist;1",
null);
}
}
// Tests a vulnerable, updatable plugin
function test18a() {
var clickToPlayNotification = PopupNotifications.getNotification("click-to-play-plugins", gTestBrowser);
ok(clickToPlayNotification, "Test 18a, Should have a click-to-play notification");
var doc = gTestBrowser.contentDocument;
var plugin = doc.getElementById("test");
var objLoadingContent = plugin.QueryInterface(Ci.nsIObjectLoadingContent);
ok(!objLoadingContent.activated, "Test 18a, Plugin should not be activated");
var overlay = doc.getAnonymousElementByAttribute(plugin, "class", "mainBox");
ok(overlay.style.visibility != "hidden", "Test 18a, Plugin overlay should exist, not be hidden");
var updateLink = doc.getAnonymousElementByAttribute(plugin, "class", "checkForUpdatesLink");
ok(updateLink.style.visibility != "hidden", "Test 18a, Plugin should have an update link");
var tabOpenListener = new TabOpenListener(Services.urlFormatter.formatURLPref("plugins.update.url"), false, false);
tabOpenListener.handleEvent = function(event) {
if (event.type == "TabOpen") {
gBrowser.tabContainer.removeEventListener("TabOpen", this, false);
this.tab = event.originalTarget;
ok(event.target.label == this.url, "Test 18a, Update link should open up the plugin check page");
gBrowser.removeTab(this.tab);
test18b();
}
};
EventUtils.synthesizeMouse(updateLink, 5, 5, {}, gTestBrowser.contentWindow);
}
function test18b() {
unregisterFakeBlocklistService();
registerFakeBlocklistService(Ci.nsIBlocklistService.STATE_VULNERABLE_NO_UPDATE);
prepareTest(test18c, gTestRoot + "plugin_test.html");
}
// Tests a vulnerable plugin with no update
function test18c() {
var clickToPlayNotification = PopupNotifications.getNotification("click-to-play-plugins", gTestBrowser);
ok(clickToPlayNotification, "Test 18c, Should have a click-to-play notification");
var doc = gTestBrowser.contentDocument;
var plugin = doc.getElementById("test");
var objLoadingContent = plugin.QueryInterface(Ci.nsIObjectLoadingContent);
ok(!objLoadingContent.activated, "Test 18c, Plugin should not be activated");
var overlay = doc.getAnonymousElementByAttribute(plugin, "class", "mainBox");
ok(overlay.style.visibility != "hidden", "Test 18c, Plugin overlay should exist, not be hidden");
var updateLink = doc.getAnonymousElementByAttribute(plugin, "class", "checkForUpdatesLink");
ok(updateLink.style.display != "block", "Test 18c, Plugin should not have an update link");
unregisterFakeBlocklistService();
var plugin = get_test_plugin();
plugin.clicktoplay = false;
finishTest();
}

View File

@ -80,20 +80,6 @@ static PRLogModuleInfo* gObjectLog = PR_NewLogModule("objlc");
#define LOG(args) PR_LOG(gObjectLog, PR_LOG_DEBUG, args)
#define LOG_ENABLED() PR_LOG_TEST(gObjectLog, PR_LOG_DEBUG)
#include "mozilla/Preferences.h"
static bool gClickToPlayPlugins = false;
static void
InitPrefCache()
{
static bool initializedPrefCache = false;
if (!initializedPrefCache) {
mozilla::Preferences::AddBoolVarCache(&gClickToPlayPlugins, "plugins.click_to_play");
}
initializedPrefCache = true;
}
class nsAsyncInstantiateEvent : public nsRunnable {
public:
nsObjectLoadingContent *mContent;
@ -181,6 +167,12 @@ nsPluginErrorEvent::Run()
case ePluginClickToPlay:
type = NS_LITERAL_STRING("PluginClickToPlay");
break;
case ePluginVulnerableUpdatable:
type = NS_LITERAL_STRING("PluginVulnerableUpdatable");
break;
case ePluginVulnerableNoUpdate:
type = NS_LITERAL_STRING("PluginVulnerableNoUpdate");
break;
case ePluginUnsupported:
type = NS_LITERAL_STRING("PluginNotFound");
break;
@ -484,7 +476,11 @@ nsresult nsObjectLoadingContent::IsPluginEnabledForType(const nsCString& aMIMETy
return rv;
}
if (!mShouldPlay) {
if (!pluginHost->IsPluginClickToPlayForType(aMIMEType.get())) {
mCTPPlayable = true;
}
if (!mCTPPlayable) {
nsCOMPtr<nsIContent> thisContent = do_QueryInterface(static_cast<nsIObjectLoadingContent*>(this));
MOZ_ASSERT(thisContent);
nsIDocument* ownerDoc = thisContent->OwnerDoc();
@ -505,12 +501,17 @@ nsresult nsObjectLoadingContent::IsPluginEnabledForType(const nsCString& aMIMETy
nsCOMPtr<nsIPermissionManager> permissionManager = do_GetService(NS_PERMISSIONMANAGER_CONTRACTID, &rv);
NS_ENSURE_SUCCESS(rv, rv);
PRUint32 permission;
rv = permissionManager->TestPermission(topUri,
"plugins",
&permission);
rv = permissionManager->TestPermission(topUri, "plugins", &permission);
NS_ENSURE_SUCCESS(rv, rv);
if (permission == nsIPermissionManager::ALLOW_ACTION) {
mShouldPlay = true;
PRUint32 state;
rv = pluginHost->GetBlocklistStateForType(aMIMEType.get(), &state);
NS_ENSURE_SUCCESS(rv, rv);
if (permission == nsIPermissionManager::ALLOW_ACTION &&
state != nsIBlocklistService::STATE_VULNERABLE_UPDATE_AVAILABLE &&
state != nsIBlocklistService::STATE_VULNERABLE_NO_UPDATE) {
mCTPPlayable = true;
} else {
return NS_ERROR_PLUGIN_CLICKTOPLAY;
}
@ -542,12 +543,9 @@ GetExtensionFromURI(nsIURI* uri, nsCString& ext)
*/
bool nsObjectLoadingContent::IsPluginEnabledByExtension(nsIURI* uri, nsCString& mimeType)
{
if (!mShouldPlay) {
return false;
}
nsCAutoString ext;
GetExtensionFromURI(uri, ext);
bool enabled = false;
if (ext.IsEmpty()) {
return false;
@ -562,9 +560,18 @@ bool nsObjectLoadingContent::IsPluginEnabledByExtension(nsIURI* uri, nsCString&
const char* typeFromExt;
if (NS_SUCCEEDED(pluginHost->IsPluginEnabledForExtension(ext.get(), typeFromExt))) {
mimeType = typeFromExt;
return true;
enabled = true;
if (!pluginHost->IsPluginClickToPlayForType(mimeType.get())) {
mCTPPlayable = true;
}
}
if (!mCTPPlayable) {
return false;
} else {
return enabled;
}
return false;
}
nsresult
@ -598,13 +605,8 @@ nsObjectLoadingContent::nsObjectLoadingContent()
, mIsStopping(false)
, mSrcStreamLoading(false)
, mFallbackReason(ePluginOtherState)
{
InitPrefCache();
// If plugins.click_to_play is false, plugins should always play
mShouldPlay = !gClickToPlayPlugins;
// If plugins.click_to_play is true, track the activated state of plugins.
mActivated = !gClickToPlayPlugins;
}
, mCTPPlayable(false)
, mActivated(false) {}
nsObjectLoadingContent::~nsObjectLoadingContent()
{
@ -617,10 +619,6 @@ nsObjectLoadingContent::~nsObjectLoadingContent()
nsresult
nsObjectLoadingContent::InstantiatePluginInstance(const char* aMimeType, nsIURI* aURI)
{
if (!mShouldPlay) {
return NS_ERROR_PLUGIN_CLICKTOPLAY;
}
// Don't do anything if we already have an active instance.
if (mInstanceOwner) {
return NS_OK;
@ -665,6 +663,14 @@ nsObjectLoadingContent::InstantiatePluginInstance(const char* aMimeType, nsIURI*
return rv;
}
if (!pluginHost->IsPluginClickToPlayForType(aMimeType)) {
mCTPPlayable = true;
}
if (!mCTPPlayable) {
return NS_ERROR_PLUGIN_CLICKTOPLAY;
}
// If you add early return(s), be sure to balance this call to
// appShell->SuspendNative() with additional call(s) to
// appShell->ReturnNative().
@ -1191,6 +1197,10 @@ nsObjectLoadingContent::ObjectState() const
switch (mFallbackReason) {
case ePluginClickToPlay:
return NS_EVENT_STATE_TYPE_CLICK_TO_PLAY;
case ePluginVulnerableUpdatable:
return NS_EVENT_STATE_VULNERABLE_UPDATABLE;
case ePluginVulnerableNoUpdate:
return NS_EVENT_STATE_VULNERABLE_NO_UPDATE;
case ePluginDisabled:
state |= NS_EVENT_STATE_HANDLER_DISABLED;
break;
@ -1933,8 +1943,10 @@ nsObjectLoadingContent::GetPluginSupportState(nsIContent* aContent,
}
PluginSupportState pluginDisabledState = GetPluginDisabledState(aContentType);
if (pluginDisabledState == ePluginClickToPlay) {
return ePluginClickToPlay;
if (pluginDisabledState == ePluginClickToPlay ||
pluginDisabledState == ePluginVulnerableUpdatable ||
pluginDisabledState == ePluginVulnerableNoUpdate) {
return pluginDisabledState;
} else if (hasAlternateContent) {
return ePluginOtherState;
} else {
@ -1946,12 +1958,28 @@ PluginSupportState
nsObjectLoadingContent::GetPluginDisabledState(const nsCString& aContentType)
{
nsresult rv = IsPluginEnabledForType(aContentType);
if (rv == NS_ERROR_PLUGIN_DISABLED)
if (rv == NS_ERROR_PLUGIN_DISABLED) {
return ePluginDisabled;
if (rv == NS_ERROR_PLUGIN_CLICKTOPLAY)
}
if (rv == NS_ERROR_PLUGIN_CLICKTOPLAY) {
PRUint32 state;
nsCOMPtr<nsIPluginHost> pluginHostCOM(do_GetService(MOZ_PLUGIN_HOST_CONTRACTID));
nsPluginHost *pluginHost = static_cast<nsPluginHost*>(pluginHostCOM.get());
if (pluginHost) {
rv = pluginHost->GetBlocklistStateForType(aContentType.get(), &state);
if (NS_SUCCEEDED(rv)) {
if (state == nsIBlocklistService::STATE_VULNERABLE_UPDATE_AVAILABLE) {
return ePluginVulnerableUpdatable;
} else if (state == nsIBlocklistService::STATE_VULNERABLE_NO_UPDATE) {
return ePluginVulnerableNoUpdate;
}
}
}
return ePluginClickToPlay;
if (rv == NS_ERROR_PLUGIN_BLOCKLISTED)
}
if (rv == NS_ERROR_PLUGIN_BLOCKLISTED) {
return ePluginBlocklisted;
}
return ePluginUnsupported;
}
@ -2213,7 +2241,7 @@ nsObjectLoadingContent::PlayPlugin()
if (!nsContentUtils::IsCallerChrome())
return NS_OK;
mShouldPlay = true;
mCTPPlayable = true;
return LoadObject(mURI, true, mContentType, true);
}

View File

@ -38,7 +38,9 @@ enum PluginSupportState {
ePluginOutdated, // The plugin is considered outdated, but not disabled
ePluginOtherState, // Something else (e.g. uninitialized or not a plugin)
ePluginCrashed,
ePluginClickToPlay // The plugin is disabled until the user clicks on it
ePluginClickToPlay, // The plugin is disabled until the user clicks on it
ePluginVulnerableUpdatable, // The plugin is vulnerable (update available)
ePluginVulnerableNoUpdate // The plugin is vulnerable (no update available)
};
/**
@ -372,9 +374,11 @@ class nsObjectLoadingContent : public nsImageLoadingContent
// it may lose the flag.
bool mNetworkCreated : 1;
// Used to keep track of whether or not a plugin should be played.
// This is used for click-to-play plugins.
bool mShouldPlay : 1;
// Used to keep track of if a plugin is blocked by click-to-play.
// True indicates the plugin is not click-to-play or it has been clicked by
// the user.
// False indicates the plugin is click-to-play and has not yet been clicked.
bool mCTPPlayable : 1;
// Used to keep track of whether or not a plugin has been played.
// This is used for click-to-play plugins.

View File

@ -242,6 +242,10 @@ private:
#define NS_EVENT_STATE_SUB_OPTIMUM NS_DEFINE_EVENT_STATE_MACRO(38)
// Content is in the sub-suboptimal region.
#define NS_EVENT_STATE_SUB_SUB_OPTIMUM NS_DEFINE_EVENT_STATE_MACRO(39)
// Handler for click to play plugin (vulnerable w/update)
#define NS_EVENT_STATE_VULNERABLE_UPDATABLE NS_DEFINE_EVENT_STATE_MACRO(40)
// Handler for click to play plugin (vulnerable w/no update)
#define NS_EVENT_STATE_VULNERABLE_NO_UPDATE NS_DEFINE_EVENT_STATE_MACRO(41)
/**
* NOTE: do not go over 63 without updating nsEventStates::InternalType!

View File

@ -5,7 +5,7 @@
#include "nsISupports.idl"
[scriptable, uuid(88e03453-a773-47ba-9d84-14f672ac99e2)]
[scriptable, uuid(a361a7e7-7f8d-4b68-91e9-30ae096460d4)]
interface nsIPluginTag : nsISupports
{
readonly attribute AUTF8String description;
@ -15,4 +15,5 @@ interface nsIPluginTag : nsISupports
readonly attribute AUTF8String name;
attribute boolean disabled;
attribute boolean blocklisted;
attribute boolean clicktoplay;
};

View File

@ -342,8 +342,10 @@ nsPluginHost::nsPluginHost()
Preferences::GetBool("plugin.override_internal_types", false);
mPluginsDisabled = Preferences::GetBool("plugin.disable", false);
mPluginsClickToPlay = Preferences::GetBool("plugins.click_to_play", false);
Preferences::AddStrongObserver(this, "plugin.disable");
Preferences::AddStrongObserver(this, "plugins.click_to_play");
nsCOMPtr<nsIObserverService> obsService =
mozilla::services::GetObserverService();
@ -1294,6 +1296,36 @@ nsPluginHost::IsPluginEnabledForType(const char* aMimeType)
return NS_OK;
}
bool
nsPluginHost::IsPluginClickToPlayForType(const char* aMimeType)
{
nsPluginTag *plugin = FindPluginForType(aMimeType, true);
if (plugin &&
(plugin->HasFlag(NS_PLUGIN_FLAG_CLICKTOPLAY) || mPluginsClickToPlay)) {
return true;
}
else {
return false;
}
}
nsresult
nsPluginHost::GetBlocklistStateForType(const char *aMimeType, PRUint32 *aState)
{
nsPluginTag *plugin = FindPluginForType(aMimeType, true);
if (plugin) {
nsCOMPtr<nsIBlocklistService> blocklist = do_GetService("@mozilla.org/extensions/blocklist;1");
if (blocklist) {
// The EmptyString()s are so we use the currently running application
// and toolkit versions
return blocklist->GetPluginBlocklistState(plugin, EmptyString(),
EmptyString(), aState);
}
}
return NS_ERROR_FAILURE;
}
// check comma delimitered extensions
static int CompareExtensions(const char *aExtensionList, const char *aExtension)
@ -2074,19 +2106,32 @@ nsresult nsPluginHost::ScanPluginsDirectory(nsIFile *pluginsDir,
EmptyString(), &state);
if (NS_SUCCEEDED(rv)) {
// If the blocklist says so then block the plugin. If the blocklist says
// it is risky and we have never seen this plugin before then disable it
if (state == nsIBlocklistService::STATE_BLOCKED)
pluginTag->Mark(NS_PLUGIN_FLAG_BLOCKLISTED);
else if (state == nsIBlocklistService::STATE_SOFTBLOCKED && !seenBefore)
enabled = false;
else if (state == nsIBlocklistService::STATE_OUTDATED && !seenBefore)
warnOutdated = true;
// If the blocklist says so, block the plugin.
// If the blocklist says it is risky and we have never seen this
// plugin before, then disable it.
// If the blocklist says this is an outdated plugin, warn about
// outdated plugins.
// If the blocklist says the plugin is one of the click-to-play
// states, set the click-to-play flag.
if (state == nsIBlocklistService::STATE_BLOCKED) {
pluginTag->Mark(NS_PLUGIN_FLAG_BLOCKLISTED);
}
if (state == nsIBlocklistService::STATE_SOFTBLOCKED && !seenBefore) {
enabled = false;
}
if (state == nsIBlocklistService::STATE_OUTDATED && !seenBefore) {
warnOutdated = true;
}
if (state == nsIBlocklistService::STATE_VULNERABLE_UPDATE_AVAILABLE ||
state == nsIBlocklistService::STATE_VULNERABLE_NO_UPDATE) {
pluginTag->Mark(NS_PLUGIN_FLAG_CLICKTOPLAY);
}
}
}
if (!enabled)
if (!enabled) {
pluginTag->UnMark(NS_PLUGIN_FLAG_ENABLED);
}
// Plugin unloading is tag-based. If we created a new tag and loaded
// the library in the process then we want to attempt to unload it here.
@ -3302,6 +3347,7 @@ NS_IMETHODIMP nsPluginHost::Observe(nsISupports *aSubject,
}
if (!nsCRT::strcmp(NS_PREFBRANCH_PREFCHANGE_TOPIC_ID, aTopic)) {
mPluginsDisabled = Preferences::GetBool("plugin.disable", false);
mPluginsClickToPlay = Preferences::GetBool("plugins.click_to_play", false);
// Unload or load plugins as needed
if (mPluginsDisabled) {
UnloadPlugins();

View File

@ -85,6 +85,8 @@ public:
nsIPluginInstanceOwner *aOwner);
nsresult IsPluginEnabledForType(const char* aMimeType);
nsresult IsPluginEnabledForExtension(const char* aExtension, const char* &aMimeType);
bool IsPluginClickToPlayForType(const char *aMimeType);
nsresult GetBlocklistStateForType(const char *aMimeType, PRUint32 *state);
nsresult GetPluginCount(PRUint32* aPluginCount);
nsresult GetPlugins(PRUint32 aPluginCount, nsIDOMPlugin** aPluginArray);
@ -281,6 +283,8 @@ private:
// set by pref plugin.disable
bool mPluginsDisabled;
// set by pref plugins.click_to_play
bool mPluginsClickToPlay;
// Any instances in this array will have valid plugin objects via GetPlugin().
// When removing an instance it might not die - be sure to null out it's plugin.

View File

@ -320,6 +320,30 @@ nsPluginTag::SetBlocklisted(bool aBlocklisted)
return NS_OK;
}
NS_IMETHODIMP
nsPluginTag::GetClicktoplay(bool *aClicktoplay)
{
*aClicktoplay = HasFlag(NS_PLUGIN_FLAG_CLICKTOPLAY);
return NS_OK;
}
NS_IMETHODIMP
nsPluginTag::SetClicktoplay(bool aClicktoplay)
{
if (HasFlag(NS_PLUGIN_FLAG_CLICKTOPLAY) == aClicktoplay) {
return NS_OK;
}
if (aClicktoplay) {
Mark(NS_PLUGIN_FLAG_CLICKTOPLAY);
} else {
UnMark(NS_PLUGIN_FLAG_CLICKTOPLAY);
}
mPluginHost->UpdatePluginInfo(nsnull);
return NS_OK;
}
void nsPluginTag::Mark(PRUint32 mask)
{
bool wasEnabled = IsEnabled();

View File

@ -27,6 +27,7 @@ struct nsPluginInfo;
#define NS_PLUGIN_FLAG_FROMCACHE 0x0004 // this plugintag info was loaded from cache
// no longer used 0x0008 // reuse only if regenerating pluginreg.dat
#define NS_PLUGIN_FLAG_BLOCKLISTED 0x0010 // this is a blocklisted plugin
#define NS_PLUGIN_FLAG_CLICKTOPLAY 0x0020 // this is a click-to-play plugin
// A linked-list of plugin information that is used for instantiating plugins
// and reflecting plugin information into JavaScript.

View File

@ -129,6 +129,10 @@ CSS_STATE_PSEUDO_CLASS(mozTypeUnsupported, ":-moz-type-unsupported",
NS_EVENT_STATE_TYPE_UNSUPPORTED)
CSS_STATE_PSEUDO_CLASS(mozHandlerClickToPlay, ":-moz-handler-clicktoplay",
NS_EVENT_STATE_TYPE_CLICK_TO_PLAY)
CSS_STATE_PSEUDO_CLASS(mozHandlerVulnerableUpdatable, ":-moz-handler-vulnerable-updatable",
NS_EVENT_STATE_VULNERABLE_UPDATABLE)
CSS_STATE_PSEUDO_CLASS(mozHandlerVulnerableNoUpdate, ":-moz-handler-vulnerable-no-update",
NS_EVENT_STATE_VULNERABLE_NO_UPDATE)
CSS_STATE_PSEUDO_CLASS(mozHandlerDisabled, ":-moz-handler-disabled",
NS_EVENT_STATE_HANDLER_DISABLED)
CSS_STATE_PSEUDO_CLASS(mozHandlerBlocked, ":-moz-handler-blocked",

View File

@ -24,6 +24,9 @@
<!-- LOCALIZATION NOTE (tapToPlayPlugin): Mobile (used for touch interfaces) only has one type of plugin possible. -->
<!ENTITY tapToPlayPlugin "Tap here to activate plugin.">
<!ENTITY clickToPlayPlugin "Click here to activate plugin.">
<!ENTITY clickToPlayPluginVulnerableUpdateAvailable "Click here to activate vulnerable plugin.">
<!ENTITY clickToPlayPluginVulnerableNoUpdate "Click here to activate vulnerable plugin (no update available).">
<!ENTITY checkForUpdates "Check for updates…">
<!ENTITY disabledPlugin "This plugin is disabled.">
<!ENTITY blockedPlugin.label "This plugin has been blocked for your protection.">

View File

@ -45,6 +45,9 @@ const DEFAULT_SEVERITY = 3;
const DEFAULT_LEVEL = 2;
const MAX_BLOCK_LEVEL = 3;
const SEVERITY_OUTDATED = 0;
const VULNERABILITYSTATUS_NONE = 0;
const VULNERABILITYSTATUS_UPDATE_AVAILABLE = 1;
const VULNERABILITYSTATUS_NO_UPDATE = 2;
var gLoggingEnabled = null;
var gBlocklistEnabled = true;
@ -831,8 +834,14 @@ Blocklist.prototype = {
toolkitVersion)) {
if (blockEntryVersion.severity >= gBlocklistLevel)
return Ci.nsIBlocklistService.STATE_BLOCKED;
if (blockEntryVersion.severity == SEVERITY_OUTDATED)
if (blockEntryVersion.severity == SEVERITY_OUTDATED) {
let vulnerabilityStatus = blockEntryVersion.vulnerabilityStatus;
if (vulnerabilityStatus == VULNERABILITYSTATUS_UPDATE_AVAILABLE)
return Ci.nsIBlocklistService.STATE_VULNERABLE_UPDATE_AVAILABLE;
if (vulnerabilityStatus == VULNERABILITYSTATUS_NO_UPDATE)
return Ci.nsIBlocklistService.STATE_VULNERABLE_NO_UPDATE;
return Ci.nsIBlocklistService.STATE_OUTDATED;
}
return Ci.nsIBlocklistService.STATE_SOFTBLOCKED;
}
}
@ -946,7 +955,8 @@ Blocklist.prototype = {
if (state == Ci.nsIBlocklistService.STATE_OUTDATED) {
gPref.setBoolPref(PREF_PLUGINS_NOTIFYUSER, true);
}
else {
else if (state != Ci.nsIBlocklistService.STATE_VULNERABLE_UPDATE_AVAILABLE &&
state != Ci.nsIBlocklistService.STATE_VULNERABLE_NO_UPDATE) {
addonList.push({
name: plugin.name,
version: plugin.version,
@ -959,6 +969,9 @@ Blocklist.prototype = {
}
}
plugin.blocklisted = state == Ci.nsIBlocklistService.STATE_BLOCKED;
if (state == Ci.nsIBlocklistService.STATE_VULNERABLE_UPDATE_AVAILABLE ||
state == Ci.nsIBlocklistService.STATE_VULNERABLE_NO_UPDATE)
plugin.clicktoplay = true;
}
if (addonList.length == 0) {
@ -1039,6 +1052,11 @@ function BlocklistItemData(versionRangeElement) {
this.severity = versionRangeElement.getAttribute("severity");
else
this.severity = DEFAULT_SEVERITY;
if (versionRangeElement && versionRangeElement.hasAttribute("vulnerabilitystatus")) {
this.vulnerabilityStatus = versionRangeElement.getAttribute("vulnerabilitystatus");
} else {
this.vulnerabilityStatus = VULNERABILITYSTATUS_NONE;
}
this.targetApps = { };
var found = false;

View File

@ -0,0 +1,26 @@
<?xml version="1.0" encoding="UTF-8"?>
<blocklist xmlns="http://www.mozilla.org/2006/addons-blocklist">
<pluginItems>
<pluginItem>
<match name="name" exp="^test_plugin_0"/>
<versionRange minVersion="0" maxVersion="*" severity="0" vulnerabilitystatus="0"/>
</pluginItem>
<pluginItem>
<match name="name" exp="^test_plugin_1"/>
<versionRange minVersion="0" maxVersion="*" severity="0" vulnerabilitystatus="1"/>
</pluginItem>
<pluginItem>
<match name="name" exp="^test_plugin_2"/>
<versionRange minVersion="0" maxVersion="*" severity="0" vulnerabilitystatus="2"/>
</pluginItem>
<pluginItem>
<match name="name" exp="^test_plugin_3"/>
<versionRange minVersion="0" maxVersion="*" vulnerabilitystatus="2"/>
</pluginItem>
<pluginItem>
<match name="name" exp="^test_plugin_4"/>
<versionRange minVersion="0" maxVersion="*" severity="1" vulnerabilitystatus="2"/>
</pluginItem>
</pluginItems>
</blocklist>

View File

@ -0,0 +1,76 @@
/* 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 nsIBLS = Components.interfaces.nsIBlocklistService;
var PLUGINS = [{
// severity=0, vulnerabilitystatus=0 -> outdated
name: "test_plugin_0",
version: "5",
disabled: false,
blocklisted: false
},
{
// severity=0, vulnerabilitystatus=1 -> update available
name: "test_plugin_1",
version: "5",
disabled: false,
blocklisted: false
},
{
// severity=0, vulnerabilitystatus=2 -> no update
name: "test_plugin_2",
version: "5",
disabled: false,
blocklisted: false
},
{
// no severity field -> severity=3 by default -> hardblock
name: "test_plugin_3",
version: "5",
disabled: false,
blocklisted: false
},
{
// severity=1, vulnerabilitystatus=2 -> softblock
name: "test_plugin_4",
version: "5",
disabled: false,
blocklisted: false
},
{
// not in the blocklist -> not blocked
name: "test_plugin_5",
version: "5",
disabled: false,
blocklisted: false
}];
function run_test() {
createAppInfo("xpcshell@tests.mozilla.org", "XPCShell", "1", "1.9");
// We cannot force the blocklist to update so just copy our test list to the profile
var blocklistFile = gProfD.clone();
blocklistFile.append("blocklist.xml");
if (blocklistFile.exists())
blocklistFile.remove(false);
var source = do_get_file("data/test_pluginBlocklistCtp.xml");
source.copyTo(gProfD, "blocklist.xml");
var blocklist = Components.classes["@mozilla.org/extensions/blocklist;1"]
.getService(nsIBLS);
do_check_true(blocklist.getPluginBlocklistState(PLUGINS[0], "1", "1.9") == nsIBLS.STATE_OUTDATED);
do_check_true(blocklist.getPluginBlocklistState(PLUGINS[1], "1", "1.9") == nsIBLS.STATE_VULNERABLE_UPDATE_AVAILABLE);
do_check_true(blocklist.getPluginBlocklistState(PLUGINS[2], "1", "1.9") == nsIBLS.STATE_VULNERABLE_NO_UPDATE);
do_check_true(blocklist.getPluginBlocklistState(PLUGINS[3], "1", "1.9") == nsIBLS.STATE_BLOCKED);
do_check_true(blocklist.getPluginBlocklistState(PLUGINS[4], "1", "1.9") == nsIBLS.STATE_SOFTBLOCKED);
do_check_true(blocklist.getPluginBlocklistState(PLUGINS[5], "1", "1.9") == nsIBLS.STATE_NOT_BLOCKED);
}

View File

@ -186,6 +186,7 @@ skip-if = os == "android"
[test_onPropertyChanged_appDisabled.js]
[test_permissions.js]
[test_plugins.js]
[test_pluginBlocklistCtp.js]
# Bug 676992: test consistently fails on Android
fail-if = os == "android"
[test_pref_properties.js]

View File

@ -26,6 +26,9 @@
<html:div class="msg msgUnsupported">&missingPlugin;</html:div>
<html:div class="msg msgTapToPlay">&tapToPlayPlugin;</html:div>
<html:div class="msg msgClickToPlay">&clickToPlayPlugin;</html:div>
<html:div class="msg msgVulnerableUpdatable">&clickToPlayPluginVulnerableUpdateAvailable;</html:div>
<html:div class="msg msgVulnerableNoUpdate">&clickToPlayPluginVulnerableNoUpdate;</html:div>
<html:div class="msg msgCheckForUpdates"><html:a class="checkForUpdatesLink" href="">&checkForUpdates;</html:a></html:div>
<html:div class="msg msgDisabled">&disabledPlugin;</html:div>
<html:div class="msg msgBlocked">&blockedPlugin.label;</html:div>
<html:div class="msg msgCrashed"><!-- set at runtime --></html:div>

View File

@ -8,14 +8,20 @@ embed:-moz-handler-disabled,
embed:-moz-handler-blocked,
embed:-moz-handler-crashed,
embed:-moz-handler-clicktoplay,
embed:-moz-handler-vulnerable-updatable,
embed:-moz-handler-vulnerable-no-update,
applet:-moz-handler-disabled,
applet:-moz-handler-blocked,
applet:-moz-handler-crashed,
applet:-moz-handler-clicktoplay,
applet:-moz-handler-vulnerable-updatable,
applet:-moz-handler-vulnerable-no-update,
object:-moz-has-handlerref:-moz-handler-disabled,
object:-moz-has-handlerref:-moz-handler-blocked,
object:-moz-handler-crashed,
object:-moz-handler-clicktoplay {
object:-moz-handler-clicktoplay,
object:-moz-handler-vulnerable-updatable,
object:-moz-handler-vulnerable-no-update {
display: inline-block;
overflow: hidden;
-moz-binding: url('chrome://mozapps/content/plugins/pluginProblem.xml#pluginProblem') !important;

View File

@ -18,12 +18,16 @@ html|applet:not([height]), html|applet[height=""] {
:-moz-type-unsupported .mainBox,
:-moz-handler-clicktoplay .mainBox,
:-moz-handler-vulnerable-updatable .mainBox,
:-moz-handler-vulnerable-no-update .mainBox,
:-moz-handler-disabled .mainBox,
:-moz-handler-blocked .mainBox {
-moz-user-focus: normal;
}
:-moz-type-unsupported .mainBox:focus,
:-moz-handler-clicktoplay .mainBox:focus,
:-moz-handler-vulnerable-updatable .mainBox:focus,
:-moz-handler-vulnerable-no-update .mainBox:focus,
:-moz-handler-disabled .mainBox:focus,
:-moz-handler-blocked .mainBox:focus {
outline: 1px dotted;
@ -47,6 +51,9 @@ html|applet:not([height]), html|applet[height=""] {
:-moz-type-unsupported .msgUnsupported,
:-moz-handler-clicktoplay .msgClickToPlay,
:-moz-handler-vulnerable-updatable .msgVulnerableUpdatable,
:-moz-handler-vulnerable-updatable .msgCheckForUpdates,
:-moz-handler-vulnerable-no-update .msgVulnerableNoUpdate,
:-moz-handler-clicktoplay .msgTapToPlay,
:-moz-handler-disabled .msgDisabled,
:-moz-handler-disabled .msgManagePlugins,

View File

@ -38,7 +38,9 @@ html|a {
:-moz-type-unsupported .icon[status="ready"] {
background-image: url(chrome://mozapps/skin/plugins/contentPluginDownload.png);
}
:-moz-handler-clicktoplay .icon {
:-moz-handler-clicktoplay .icon,
:-moz-handler-vulnerable-updatable .icon,
:-moz-handler-vulnerable-no-update .icon {
background-image: url(chrome://mozapps/skin/plugins/contentPluginClickToPlay.png);
}
:-moz-handler-disabled .icon {
@ -64,8 +66,9 @@ html|a {
text-shadow: rgba(0,0,0,0.8) 0 0 3.5px;
}
:-moz-handler-clicktoplay,
.msgClickToPlay {
:-moz-handler-clicktoplay .msgClickToPlay,
:-moz-handler-vulnerable-updatable .msgClickToPlay,
:-moz-handler-vulnerable-no-update .msgClickToPlay {
cursor: pointer;
}

View File

@ -38,7 +38,9 @@ html|a {
:-moz-type-unsupported .icon[status="ready"] {
background-image: url(chrome://mozapps/skin/plugins/contentPluginDownload.png);
}
:-moz-handler-clicktoplay .icon {
:-moz-handler-clicktoplay .icon,
:-moz-handler-vulnerable-updatable .icon,
:-moz-handler-vulnerable-no-update .icon {
background-image: url(chrome://mozapps/skin/plugins/contentPluginClickToPlay.png);
}
:-moz-handler-disabled .icon {
@ -64,8 +66,9 @@ html|a {
text-shadow: rgba(0,0,0,0.8) 0 0 3.5px;
}
:-moz-handler-clicktoplay,
.msgClickToPlay {
:-moz-handler-clicktoplay .msgClickToPlay,
:-moz-handler-vulnerable-updatable .msgClickToPlay,
:-moz-handler-vulnerable-no-update .msgClickToPlay {
cursor: pointer;
}

View File

@ -9,7 +9,7 @@
interface nsIPluginTag;
interface nsIVariant;
[scriptable, uuid(31845f85-718a-4581-a672-a45c0327cb21)]
[scriptable, uuid(cbba15b8-316d-4ae6-8ed9-fe9cf8386730)]
interface nsIBlocklistService : nsISupports
{
// Indicates that the item does not appear in the blocklist.
@ -22,6 +22,10 @@ interface nsIBlocklistService : nsISupports
// Indicates that the item is considered outdated, and there is a known
// update available.
const unsigned long STATE_OUTDATED = 3;
// Indicates that the item is vulnerable and there is an update.
const unsigned long STATE_VULNERABLE_UPDATE_AVAILABLE = 4;
// Indicates that the item is vulnerable and there is no update.
const unsigned long STATE_VULNERABLE_NO_UPDATE = 5;
/**
* Determine if an item is blocklisted