From e680d73b9697518f180c3000c8bab79f2d372d0b Mon Sep 17 00:00:00 2001 From: Brad Lassey Date: Mon, 3 Aug 2015 19:24:35 -0400 Subject: [PATCH] bug 1186948 - remove plugins that are click-to-play from navigator.plugins (restricted to Flash) r=jst --- browser/modules/PluginContent.jsm | 52 ++++++++++++++++++++++++++++++ dom/base/nsPluginArray.cpp | 41 +++++++++++++++++++++-- dom/base/nsPluginArray.h | 4 +++ dom/plugins/base/nsIPluginHost.idl | 12 +++++++ dom/plugins/base/nsPluginHost.cpp | 14 ++++++-- dom/plugins/base/nsPluginTags.cpp | 2 +- dom/plugins/base/nsPluginTags.h | 6 ++++ 7 files changed, 124 insertions(+), 7 deletions(-) diff --git a/browser/modules/PluginContent.jsm b/browser/modules/PluginContent.jsm index fea664163b4c..0d93f970cf10 100644 --- a/browser/modules/PluginContent.jsm +++ b/browser/modules/PluginContent.jsm @@ -53,6 +53,8 @@ PluginContent.prototype = { global.addMessageListener("BrowserPlugins:NPAPIPluginProcessCrashed", this); global.addMessageListener("BrowserPlugins:CrashReportSubmitted", this); global.addMessageListener("BrowserPlugins:Test:ClearCrashData", this); + + Services.obs.addObserver(this, "Plugin::HiddenPluginTouched", false); }, uninit: function() { @@ -75,6 +77,8 @@ PluginContent.prototype = { global.removeMessageListener("BrowserPlugins:Test:ClearCrashData", this); delete this.global; delete this.content; + + Services.obs.removeObserver(this, "Plugin::HiddenPluginTouched"); }, receiveMessage: function (msg) { @@ -116,6 +120,15 @@ PluginContent.prototype = { } }, + observe: function observe(aSubject, aTopic, aData) { + let pluginTag = aSubject; + if (aTopic == "Plugin::HiddenPluginTouched") { + this._showClickToPlayNotification(pluginTag, false); + } else { + Cu.reportError("unknown topic observed: " + aTopic); + } + }, + onPageShow: function (event) { // Ignore events that aren't from the main document. if (!this.content || event.target != this.content.document) { @@ -194,6 +207,45 @@ PluginContent.prototype = { }; }, + _getPluginInfoForTag: function (pluginTag, tagMimetype, fallbackType) { + let pluginHost = Cc["@mozilla.org/plugin/host;1"].getService(Ci.nsIPluginHost); + + let pluginName = gNavigatorBundle.GetStringFromName("pluginInfo.unknownPlugin"); + let permissionString = null; + let blocklistState = null; + + if (pluginTag) { + pluginName = BrowserUtils.makeNicePluginName(pluginTag.name); + + permissionString = pluginHost.getPermissionStringForTag(pluginTag); + blocklistState = pluginTag.blocklistState; + + // Convert this from nsIPluginTag so it can be serialized. + let properties = ["name", "description", "filename", "version", "enabledState", "niceName"]; + let pluginTagCopy = {}; + for (let prop of properties) { + pluginTagCopy[prop] = pluginTag[prop]; + } + pluginTag = pluginTagCopy; + + // Make state-softblocked == state-notblocked for our purposes, + // they have the same UI. STATE_OUTDATED should not exist for plugin + // items, but let's alias it anyway, just in case. + if (blocklistState == Ci.nsIBlocklistService.STATE_SOFTBLOCKED || + blocklistState == Ci.nsIBlocklistService.STATE_OUTDATED) { + blocklistState = Ci.nsIBlocklistService.STATE_NOT_BLOCKED; + } + } + + return { mimetype: tagMimetype, + pluginName: pluginName, + pluginTag: pluginTag, + permissionString: permissionString, + fallbackType: fallbackType, + blocklistState: blocklistState, + }; + }, + /** * Update the visibility of the plugin overlay. */ diff --git a/dom/base/nsPluginArray.cpp b/dom/base/nsPluginArray.cpp index b6630c78b333..610b32bfe973 100644 --- a/dom/base/nsPluginArray.cpp +++ b/dom/base/nsPluginArray.cpp @@ -19,6 +19,8 @@ #include "nsIWeakReference.h" #include "mozilla/Services.h" #include "nsIInterfaceRequestorUtils.h" +#include "nsIPermissionManager.h" +#include "nsIDocument.h" using namespace mozilla; using namespace mozilla::dom; @@ -66,7 +68,8 @@ NS_INTERFACE_MAP_END NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE(nsPluginArray, mWindow, - mPlugins) + mPlugins, + mCTPPlugins) static void GetPluginMimeTypes(const nsTArray >& aPlugins, @@ -146,6 +149,7 @@ nsPluginArray::Refresh(bool aReloadDocuments) } mPlugins.Clear(); + mCTPPlugins.Clear(); nsCOMPtr navigator = mWindow->GetNavigator(); @@ -221,6 +225,13 @@ nsPluginArray::NamedGetter(const nsAString& aName, bool &aFound) nsPluginElement* plugin = FindPlugin(mPlugins, aName); aFound = (plugin != nullptr); + if (!aFound) { + nsPluginElement* hiddenPlugin = FindPlugin(mCTPPlugins, aName); + if (hiddenPlugin) { + nsCOMPtr obs = mozilla::services::GetObserverService(); + obs->NotifyObservers(hiddenPlugin->PluginTag(), "Plugin::HiddenPluginTouched", nsString(aName).get()); + } + } return plugin; } @@ -282,7 +293,7 @@ operator<(const RefPtr& lhs, void nsPluginArray::EnsurePlugins() { - if (!mPlugins.IsEmpty()) { + if (!mPlugins.IsEmpty() || !mCTPPlugins.IsEmpty()) { // We already have an array of plugin elements. return; } @@ -299,7 +310,31 @@ nsPluginArray::EnsurePlugins() // need to wrap each of these with a nsPluginElement, which is // scriptable. for (uint32_t i = 0; i < pluginTags.Length(); ++i) { - mPlugins.AppendElement(new nsPluginElement(mWindow, pluginTags[i])); + nsCOMPtr pluginTag = do_QueryInterface(pluginTags[i]); + if (!pluginTag) { + mPlugins.AppendElement(new nsPluginElement(mWindow, pluginTags[i])); + } else if (pluginTag->IsActive()) { + uint32_t permission = nsIPermissionManager::ALLOW_ACTION; + if (pluginTag->IsClicktoplay()) { + nsCString name; + pluginTag->GetName(name); + if (NS_LITERAL_CSTRING("Shockwave Flash").Equals(name)) { + RefPtr pluginHost = nsPluginHost::GetInst(); + nsCString permString; + nsresult rv = pluginHost->GetPermissionStringForTag(pluginTag, 0, permString); + if (rv == NS_OK) { + nsIPrincipal* principal = mWindow->GetExtantDoc()->NodePrincipal(); + nsCOMPtr permMgr = services::GetPermissionManager(); + permMgr->TestPermissionFromPrincipal(principal, permString.get(), &permission); + } + } + } + if (permission == nsIPermissionManager::ALLOW_ACTION) { + mPlugins.AppendElement(new nsPluginElement(mWindow, pluginTags[i])); + } else { + mCTPPlugins.AppendElement(new nsPluginElement(mWindow, pluginTags[i])); + } + } } // Alphabetize the enumeration order of non-hidden plugins to reduce diff --git a/dom/base/nsPluginArray.h b/dom/base/nsPluginArray.h index 526bb73b6769..8e02c9a711ac 100644 --- a/dom/base/nsPluginArray.h +++ b/dom/base/nsPluginArray.h @@ -60,6 +60,10 @@ private: nsCOMPtr mWindow; nsTArray > mPlugins; + /* A separate list of click-to-play plugins that we don't tell content + * about but keep track of so we can still prompt the user to click to play. + */ + nsTArray > mCTPPlugins; }; class nsPluginElement final : public nsISupports, diff --git a/dom/plugins/base/nsIPluginHost.idl b/dom/plugins/base/nsIPluginHost.idl index ae349bea2a26..d1124aa25959 100644 --- a/dom/plugins/base/nsIPluginHost.idl +++ b/dom/plugins/base/nsIPluginHost.idl @@ -99,6 +99,18 @@ interface nsIPluginHost : nsISupports ACString getPermissionStringForType(in AUTF8String mimeType, [optional] in uint32_t excludeFlags); + /** + * Get the "permission string" for the plugin. This is a string that can be + * passed to the permission manager to see whether the plugin is allowed to + * run, for example. This will typically be based on the plugin's "nice name" + * and its blocklist state. + * + * @tag The tage we're interested in + * @excludeFlags Set of the EXCLUDE_* flags above, defaulting to EXCLUDE_NONE. + */ + ACString getPermissionStringForTag(in nsIPluginTag tag, + [optional] in uint32_t excludeFlags); + /** * Get the nsIPluginTag for this MIME type. This method works with both * enabled and disabled/blocklisted plugins, but an enabled plugin will diff --git a/dom/plugins/base/nsPluginHost.cpp b/dom/plugins/base/nsPluginHost.cpp index 914d47739156..7abbaab09648 100644 --- a/dom/plugins/base/nsPluginHost.cpp +++ b/dom/plugins/base/nsPluginHost.cpp @@ -1110,11 +1110,19 @@ nsPluginHost::GetPermissionStringForType(const nsACString &aMimeType, nsresult rv = GetPluginTagForType(aMimeType, aExcludeFlags, getter_AddRefs(tag)); NS_ENSURE_SUCCESS(rv, rv); - NS_ENSURE_TRUE(tag, NS_ERROR_FAILURE); + return GetPermissionStringForTag(tag, aExcludeFlags, aPermissionString); +} + +NS_IMETHODIMP +nsPluginHost::GetPermissionStringForTag(nsIPluginTag* aTag, + uint32_t aExcludeFlags, + nsACString &aPermissionString) +{ + NS_ENSURE_TRUE(aTag, NS_ERROR_FAILURE); aPermissionString.Truncate(); uint32_t blocklistState; - rv = tag->GetBlocklistState(&blocklistState); + nsresult rv = aTag->GetBlocklistState(&blocklistState); NS_ENSURE_SUCCESS(rv, rv); if (blocklistState == nsIBlocklistService::STATE_VULNERABLE_UPDATE_AVAILABLE || @@ -1126,7 +1134,7 @@ nsPluginHost::GetPermissionStringForType(const nsACString &aMimeType, } nsCString niceName; - rv = tag->GetNiceName(niceName); + rv = aTag->GetNiceName(niceName); NS_ENSURE_SUCCESS(rv, rv); NS_ENSURE_TRUE(!niceName.IsEmpty(), NS_ERROR_FAILURE); diff --git a/dom/plugins/base/nsPluginTags.cpp b/dom/plugins/base/nsPluginTags.cpp index 326b6ddd91e6..1620935ecb3b 100644 --- a/dom/plugins/base/nsPluginTags.cpp +++ b/dom/plugins/base/nsPluginTags.cpp @@ -323,7 +323,7 @@ nsPluginTag::~nsPluginTag() NS_ASSERTION(!mNext, "Risk of exhausting the stack space, bug 486349"); } -NS_IMPL_ISUPPORTS(nsPluginTag, nsIPluginTag, nsIInternalPluginTag) +NS_IMPL_ISUPPORTS(nsPluginTag, nsPluginTag, nsIInternalPluginTag, nsIPluginTag) void nsPluginTag::InitMime(const char* const* aMimeTypes, const char* const* aMimeDescriptions, diff --git a/dom/plugins/base/nsPluginTags.h b/dom/plugins/base/nsPluginTags.h index 9adc628688ff..71b1d7154d9d 100644 --- a/dom/plugins/base/nsPluginTags.h +++ b/dom/plugins/base/nsPluginTags.h @@ -30,6 +30,9 @@ struct FakePluginTagInit; { 0xe8fdd227, 0x27da, 0x46ee, \ { 0xbe, 0xf3, 0x1a, 0xef, 0x5a, 0x8f, 0xc5, 0xb4 } } +#define NS_PLUGINTAG_IID \ + { 0xcce2e8b9, 0x9702, 0x4d4b, \ + { 0xbe, 0xa4, 0x7c, 0x1e, 0x13, 0x1f, 0xaf, 0x78 } } class nsIInternalPluginTag : public nsIPluginTag { public: @@ -90,6 +93,8 @@ NS_DEFINE_STATIC_IID_ACCESSOR(nsIInternalPluginTag, NS_IINTERNALPLUGINTAG_IID) class nsPluginTag final : public nsIInternalPluginTag { public: + NS_DECLARE_STATIC_IID_ACCESSOR(NS_PLUGINTAG_IID) + NS_DECL_ISUPPORTS NS_DECL_NSIPLUGINTAG @@ -192,6 +197,7 @@ private: static uint32_t sNextId; }; +NS_DEFINE_STATIC_IID_ACCESSOR(nsPluginTag, NS_PLUGINTAG_IID) // A class representing "fake" plugin tags; that is plugin tags not // corresponding to actual NPAPI plugins. In practice these are all