Bug 757726 - Part 6a: Add support for cloaking plugin names in navigator.plugins and navigator.mimeTypes enumeration. r=johns sr=bsmedberg

This commit is contained in:
Chris Peterson 2013-10-31 22:19:09 -07:00
parent 66215cc9ce
commit cf1496031d
8 changed files with 213 additions and 77 deletions

View File

@ -25,13 +25,13 @@ NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(nsMimeTypeArray)
NS_INTERFACE_MAP_ENTRY(nsISupports)
NS_INTERFACE_MAP_END
NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE_2(nsMimeTypeArray,
NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE_3(nsMimeTypeArray,
mWindow,
mMimeTypes)
mMimeTypes,
mHiddenMimeTypes)
nsMimeTypeArray::nsMimeTypeArray(nsPIDOMWindow* aWindow)
: mWindow(aWindow),
mPluginMimeTypeCount(0)
: mWindow(aWindow)
{
SetIsDOMBinding();
}
@ -50,7 +50,7 @@ void
nsMimeTypeArray::Refresh()
{
mMimeTypes.Clear();
mPluginMimeTypeCount = 0;
mHiddenMimeTypes.Clear();
}
nsPIDOMWindow*
@ -81,9 +81,7 @@ nsMimeTypeArray::IndexedGetter(uint32_t aIndex, bool &aFound)
EnsurePluginMimeTypes();
MOZ_ASSERT(mMimeTypes.Length() >= mPluginMimeTypeCount);
if (aIndex >= mPluginMimeTypeCount) {
if (aIndex >= mMimeTypes.Length()) {
return nullptr;
}
@ -92,6 +90,20 @@ nsMimeTypeArray::IndexedGetter(uint32_t aIndex, bool &aFound)
return mMimeTypes[aIndex];
}
static nsMimeType*
FindMimeType(const nsTArray<nsRefPtr<nsMimeType> >& aMimeTypes,
const nsAString& aType)
{
for (uint32_t i = 0; i < aMimeTypes.Length(); ++i) {
nsMimeType* mimeType = aMimeTypes[i];
if (aType.Equals(mimeType->Type())) {
return mimeType;
}
}
return nullptr;
}
nsMimeType*
nsMimeTypeArray::NamedGetter(const nsAString& aName, bool &aFound)
{
@ -99,12 +111,14 @@ nsMimeTypeArray::NamedGetter(const nsAString& aName, bool &aFound)
EnsurePluginMimeTypes();
for (uint32_t i = 0; i < mMimeTypes.Length(); ++i) {
if (aName.Equals(mMimeTypes[i]->Type())) {
aFound = true;
nsMimeType* mimeType = FindMimeType(mMimeTypes, aName);
if (!mimeType) {
mimeType = FindMimeType(mHiddenMimeTypes, aName);
}
return mMimeTypes[i];
}
if (mimeType) {
aFound = true;
return mimeType;
}
// Now let's check with the MIME service.
@ -148,8 +162,10 @@ nsMimeTypeArray::NamedGetter(const nsAString& aName, bool &aFound)
// If we got here, we support this type! Say so.
aFound = true;
// We don't want navigator.mimeTypes enumeration to expose MIME types with
// application handlers, so add them to the list of hidden MIME types.
nsMimeType *mt = new nsMimeType(mWindow, aName);
mMimeTypes.AppendElement(mt);
mHiddenMimeTypes.AppendElement(mt);
return mt;
}
@ -159,9 +175,7 @@ nsMimeTypeArray::Length()
{
EnsurePluginMimeTypes();
MOZ_ASSERT(mMimeTypes.Length() >= mPluginMimeTypeCount);
return mPluginMimeTypeCount;
return mMimeTypes.Length();
}
void
@ -177,7 +191,7 @@ nsMimeTypeArray::GetSupportedNames(nsTArray< nsString >& aRetval)
void
nsMimeTypeArray::EnsurePluginMimeTypes()
{
if (!mMimeTypes.IsEmpty() || !mWindow) {
if (!mMimeTypes.IsEmpty() || !mHiddenMimeTypes.IsEmpty() || !mWindow) {
return;
}
@ -195,9 +209,7 @@ nsMimeTypeArray::EnsurePluginMimeTypes()
return;
}
pluginArray->GetMimeTypes(mMimeTypes);
mPluginMimeTypeCount = mMimeTypes.Length();
pluginArray->GetMimeTypes(mMimeTypes, mHiddenMimeTypes);
}
NS_IMPL_CYCLE_COLLECTION_ROOT_NATIVE(nsMimeType, AddRef)

View File

@ -46,15 +46,16 @@ protected:
nsCOMPtr<nsPIDOMWindow> mWindow;
// mMimeTypes contains all mime types handled by plugins followed by
// any other mime types that we handle internally and have been
// looked up before.
// mMimeTypes contains MIME types handled by non-hidden plugins, those
// popular plugins that must be exposed in navigator.plugins enumeration to
// avoid breaking web content. Likewise, mMimeTypes are exposed in
// navigator.mimeTypes enumeration.
nsTArray<nsRefPtr<nsMimeType> > mMimeTypes;
// mPluginMimeTypeCount is the number of plugin mime types that we
// have in mMimeTypes. The plugin mime types are always at the
// beginning of the list.
uint32_t mPluginMimeTypeCount;
// mHiddenMimeTypes contains MIME types handled by plugins hidden from
// navigator.plugins enumeration or by an OS PreferredApplicationHandler.
// mHiddenMimeTypes are hidden from navigator.mimeTypes enumeration.
nsTArray<nsRefPtr<nsMimeType> > mHiddenMimeTypes;
};
class nsMimeType MOZ_FINAL : public nsWrapperCache

View File

@ -5,8 +5,11 @@
#include "nsPluginArray.h"
#include "mozilla/Preferences.h"
#include "mozilla/dom/PluginArrayBinding.h"
#include "mozilla/dom/PluginBinding.h"
#include "nsCharSeparatedTokenizer.h"
#include "nsMimeTypeArray.h"
#include "Navigator.h"
#include "nsIDocShell.h"
@ -63,14 +66,27 @@ NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(nsPluginArray)
NS_INTERFACE_MAP_ENTRY(nsISupportsWeakReference)
NS_INTERFACE_MAP_END
NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE_2(nsPluginArray,
NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE_3(nsPluginArray,
mWindow,
mPlugins)
mPlugins,
mHiddenPlugins)
static void
GetPluginMimeTypes(const nsTArray<nsRefPtr<nsPluginElement> >& aPlugins,
nsTArray<nsRefPtr<nsMimeType> >& aMimeTypes)
{
for (uint32_t i = 0; i < aPlugins.Length(); ++i) {
nsPluginElement *plugin = aPlugins[i];
aMimeTypes.AppendElements(plugin->MimeTypes());
}
}
void
nsPluginArray::GetMimeTypes(nsTArray<nsRefPtr<nsMimeType> >& aMimeTypes)
nsPluginArray::GetMimeTypes(nsTArray<nsRefPtr<nsMimeType> >& aMimeTypes,
nsTArray<nsRefPtr<nsMimeType> >& aHiddenMimeTypes)
{
aMimeTypes.Clear();
aHiddenMimeTypes.Clear();
if (!AllowPlugins()) {
return;
@ -78,10 +94,8 @@ nsPluginArray::GetMimeTypes(nsTArray<nsRefPtr<nsMimeType> >& aMimeTypes)
EnsurePlugins();
for (uint32_t i = 0; i < mPlugins.Length(); ++i) {
nsPluginElement *plugin = mPlugins[i];
aMimeTypes.AppendElements(plugin->MimeTypes());
}
GetPluginMimeTypes(mPlugins, aMimeTypes);
GetPluginMimeTypes(mHiddenPlugins, aHiddenMimeTypes);
}
nsPluginElement*
@ -121,12 +135,14 @@ nsPluginArray::Refresh(bool aReloadDocuments)
// happens, and therefore the lengths will be in sync only when
// the both arrays contain the same plugin tags (though as
// different types).
if (newPluginTags.Length() == mPlugins.Length()) {
uint32_t pluginCount = mPlugins.Length() + mHiddenPlugins.Length();
if (newPluginTags.Length() == pluginCount) {
return;
}
}
mPlugins.Clear();
mHiddenPlugins.Clear();
nsCOMPtr<nsIDOMNavigator> navigator;
mWindow->GetNavigator(getter_AddRefs(navigator));
@ -169,6 +185,23 @@ nsPluginArray::Invalidate()
}
}
static nsPluginElement*
FindPlugin(const nsTArray<nsRefPtr<nsPluginElement> >& aPlugins,
const nsAString& aName)
{
for (uint32_t i = 0; i < aPlugins.Length(); ++i) {
nsAutoString pluginName;
nsPluginElement* plugin = aPlugins[i];
plugin->GetName(pluginName);
if (pluginName.Equals(aName)) {
return plugin;
}
}
return nullptr;
}
nsPluginElement*
nsPluginArray::NamedGetter(const nsAString& aName, bool &aFound)
{
@ -180,19 +213,13 @@ nsPluginArray::NamedGetter(const nsAString& aName, bool &aFound)
EnsurePlugins();
for (uint32_t i = 0; i < mPlugins.Length(); ++i) {
nsAutoString pluginName;
nsPluginElement* plugin = mPlugins[i];
plugin->GetName(pluginName);
if (pluginName.Equals(aName)) {
aFound = true;
return plugin;
}
nsPluginElement* plugin = FindPlugin(mPlugins, aName);
if (!plugin) {
plugin = FindPlugin(mHiddenPlugins, aName);
}
return nullptr;
aFound = (plugin != nullptr);
return plugin;
}
uint32_t
@ -242,10 +269,32 @@ nsPluginArray::AllowPlugins() const
return docShell && docShell->PluginsAllowedInCurrentDoc();
}
static bool
HasStringPrefix(const nsCString& str, const nsACString& prefix) {
return str.Compare(prefix.BeginReading(), false, prefix.Length()) == 0;
}
static bool
IsPluginEnumerable(const nsTArray<nsCString>& enumerableNames,
const nsPluginTag* pluginTag)
{
const nsCString& pluginName = pluginTag->mName;
const uint32_t length = enumerableNames.Length();
for (uint32_t i = 0; i < length; i++) {
const nsCString& name = enumerableNames[i];
if (HasStringPrefix(pluginName, name)) {
return true; // don't hide plugin
}
}
return false; // hide plugin!
}
void
nsPluginArray::EnsurePlugins()
{
if (!mPlugins.IsEmpty()) {
if (!mPlugins.IsEmpty() || !mHiddenPlugins.IsEmpty()) {
// We already have an array of plugin elements.
return;
}
@ -259,10 +308,36 @@ nsPluginArray::EnsurePlugins()
nsTArray<nsRefPtr<nsPluginTag> > pluginTags;
pluginHost->GetPlugins(pluginTags);
nsTArray<nsCString> enumerableNames;
const nsAdoptingCString& enumerableNamesPref =
Preferences::GetCString("plugins.enumerable_names");
bool disablePluginHiding = !enumerableNamesPref ||
enumerableNamesPref.EqualsLiteral("*");
if (!disablePluginHiding) {
nsCCharSeparatedTokenizer tokens(enumerableNamesPref, ',');
while (tokens.hasMoreTokens()) {
const nsCSubstring& token = tokens.nextToken();
if (!token.IsEmpty()) {
enumerableNames.AppendElement(token);
}
}
}
// 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]));
nsPluginTag* pluginTag = pluginTags[i];
// Add the plugin to the list of hidden plugins or non-hidden plugins?
nsTArray<nsRefPtr<nsPluginElement> >& pluginArray =
(disablePluginHiding || IsPluginEnumerable(enumerableNames, pluginTag))
? mPlugins
: mHiddenPlugins;
pluginArray.AppendElement(new nsPluginElement(mWindow, pluginTag));
}
}

View File

@ -43,7 +43,8 @@ public:
void Init();
void Invalidate();
void GetMimeTypes(nsTArray<nsRefPtr<nsMimeType> >& aMimeTypes);
void GetMimeTypes(nsTArray<nsRefPtr<nsMimeType> >& aMimeTypes,
nsTArray<nsRefPtr<nsMimeType> >& aHiddenMimeTypes);
// PluginArray WebIDL methods
@ -60,7 +61,18 @@ private:
void EnsurePlugins();
nsCOMPtr<nsPIDOMWindow> mWindow;
// Many sites check whether a particular plugin is installed by enumerating
// all navigator.plugins, checking each plugin's name. These sites should
// just check navigator.plugins["Popular Plugin Name"] instead. mPlugins
// contains those popular plugins that must be exposed in navigator.plugins
// enumeration to avoid breaking web content.
nsTArray<nsRefPtr<nsPluginElement> > mPlugins;
// mHiddenPlugins contains plugins that can be queried by
// navigator.plugins["Hidden Plugin Name"] but do not need to be exposed in
// navigator.plugins enumeration.
nsTArray<nsRefPtr<nsPluginElement> > mHiddenPlugins;
};
class nsPluginElement MOZ_FINAL : public nsISupports,

View File

@ -9,24 +9,60 @@
<body onload="run()">
<script class="testbody" type="application/javascript">
"use strict";
SimpleTest.waitForExplicitFinish();
setTestPluginEnabledState(SpecialPowers.Ci.nsIPluginTag.STATE_ENABLED);
setTestPluginEnabledState(SpecialPowers.Ci.nsIPluginTag.STATE_ENABLED, "Second Test Plug-in");
function findPlugin(pluginName) {
for (var i = 0; i < navigator.plugins.length; i++) {
var plugin = navigator.plugins[i];
if (plugin.name === pluginName) {
return plugin;
}
}
return null;
}
function findMimeType(mimeTypeType) {
for (var i = 0; i < navigator.mimeTypes.length; i++) {
var mimeType = navigator.mimeTypes[i];
if (mimeType.type === mimeTypeType) {
return mimeType;
}
}
return null;
}
function run() {
var foundFirstPlugin = false;
var foundSecondPlugin = false;
for (var index in navigator.plugins) {
var plugin = navigator.plugins[index];
if (plugin.name == "Test Plug-in") foundFirstPlugin = true;
if (plugin.name == "Second Test Plug-in") foundSecondPlugin = true;
}
ok(foundFirstPlugin, "Should have a plugin named 'Test Plug-in'");
ok(foundSecondPlugin, "Should have a plugin named 'Second Test Plug-in'");
// Add "Test Plug-in" (but not "Second Test Plug-in") to the list of
// unhidden plugins. This test must modify the "plugins.enumerable_names"
// pref BEFORE accessing the navigator.plugins or navigator.mimeTypes
// arrays because they only read the pref when they first initialize
// their internal arrays!
var prefs = SpecialPowers.Cc["@mozilla.org/preferences-service;1"].getService(SpecialPowers.Ci.nsIPrefBranch);
var defaultEnumerableNamesPref = prefs.getCharPref("plugins.enumerable_names");
prefs.setCharPref("plugins.enumerable_names", defaultEnumerableNamesPref + ",Test Plug-in");
var pluginElement = document.getElementById("plugin");
is(pluginElement.identifierToStringTest("foo"), "foo", "Should be able to call a function provided by the plugin");
ok(navigator.plugins["Test Plug-in"], "Should have queried a non-hidden plugin named 'Test Plug-in'");
ok(navigator.plugins["Second Test Plug-in"], "Should have queried a hidden plugin named 'Test Plug-in'");
ok(findPlugin("Test Plug-in"), "Should have found a non-hidden plugin named 'Test Plug-in'");
ok(!findPlugin("Second Test Plug-in"), "Should NOT found a hidden plugin named 'Test Plug-in'");
ok(navigator.mimeTypes["application/x-test"], "Should have queried a non-hidden MIME type named 'application/x-test'");
ok(navigator.mimeTypes["application/x-second-test"], "Should have queried a MIME type named 'application/x-second-test'");
ok(findMimeType("application/x-test"), "Should have found a non-hidden MIME type named 'application/x-test'");
ok(!findMimeType("application/x-second-test"), "Should NOT have found a MIME type named 'application/x-second-test'");
// Restore original pref to hide "Test Plug-in" and "Second Test Plug-in".
prefs.setCharPref("plugins.enumerable_names", defaultEnumerableNamesPref);
SimpleTest.finish();
}
</script>

View File

@ -19,14 +19,13 @@ https://bugzilla.mozilla.org/show_bug.cgi?id=427744
/** Test for Bug 427744 **/
var found = false;
for (var i = 0; i < navigator.plugins.length; i++) {
if (navigator.plugins[i].name == "Test Plug-in") {
found = true;
is(navigator.plugins[i].version, "1.0.0.0", "Should have seen the right version");
}
}
ok(found, "Should have seen the test plugin");
var firstPlugin = navigator.plugins["Test Plug-in"];
ok(firstPlugin, "Should have seen the test plugin");
is(firstPlugin.version, "1.0.0.0", "Should have seen the right test plugin version");
var secondPlugin = navigator.plugins["Second Test Plug-in"];
ok(secondPlugin, "Should have seen the second test plugin");
is(secondPlugin.version, "1.0.0.0", "Should have seen the right second test plugin version");
</script>
</pre>

View File

@ -636,17 +636,9 @@ function BuildConditionSandbox(aURL) {
// see if we have the test plugin available,
// and set a sandox prop accordingly
sandbox.haveTestPlugin = false;
var navigator = gContainingWindow.navigator;
for (var i = 0; i < navigator.mimeTypes.length; i++) {
if (navigator.mimeTypes[i].type == "application/x-test" &&
navigator.mimeTypes[i].enabledPlugin != null &&
navigator.mimeTypes[i].enabledPlugin.name == "Test Plug-in") {
sandbox.haveTestPlugin = true;
break;
}
}
var testPlugin = navigator.plugins["Test Plug-in"];
sandbox.haveTestPlugin = !!testPlugin;
// Set a flag on sandbox if the windows default theme is active
var box = gContainingWindow.document.createElement("box");

View File

@ -2032,6 +2032,15 @@ pref("hangmonitor.timeout", 0);
pref("plugins.load_appdir_plugins", false);
// If true, plugins will be click to play
pref("plugins.click_to_play", false);
// A comma-delimited list of plugin name prefixes matching plugins that will be
// exposed when enumerating navigator.plugins[]. For example, prefix "Shockwave"
// matches both Adobe Flash Player ("Shockwave Flash") and Adobe Shockwave
// Player ("Shockwave for Director"). To hide all plugins from enumeration, use
// the empty string "" to match no plugin names. To allow all plugins to be
// enumerated, use the string "*" to match all plugin names.
pref("plugins.enumerable_names", "*");
// The default value for nsIPluginTag.enabledState (STATE_ENABLED = 2)
pref("plugin.default.state", 2);