summaryrefslogtreecommitdiffstats
path: root/webkit/glue/plugins/plugin_list_posix.cc
diff options
context:
space:
mode:
Diffstat (limited to 'webkit/glue/plugins/plugin_list_posix.cc')
-rw-r--r--webkit/glue/plugins/plugin_list_posix.cc267
1 files changed, 267 insertions, 0 deletions
diff --git a/webkit/glue/plugins/plugin_list_posix.cc b/webkit/glue/plugins/plugin_list_posix.cc
new file mode 100644
index 0000000..1fbd76f
--- /dev/null
+++ b/webkit/glue/plugins/plugin_list_posix.cc
@@ -0,0 +1,267 @@
+// Copyright (c) 2010 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "webkit/glue/plugins/plugin_list.h"
+
+#include "base/file_util.h"
+#include "base/path_service.h"
+#include "base/sha1.h"
+#include "base/string_util.h"
+#include "build/build_config.h"
+
+namespace {
+
+// We build up a list of files and mtimes so we can sort them.
+typedef std::pair<FilePath, base::Time> FileAndTime;
+typedef std::vector<FileAndTime> FileTimeList;
+
+// Comparator used to sort by descending mtime then ascending filename.
+bool CompareTime(const FileAndTime& a, const FileAndTime& b) {
+ if (a.second == b.second) {
+ // Fall back on filename sorting, just to make the predicate valid.
+ return a.first < b.first;
+ }
+
+ // Sort by mtime, descending.
+ return a.second > b.second;
+}
+
+// Return true if |path| matches a known (file size, sha1sum) pair.
+// The use of the file size is an optimization so we don't have to read in
+// the entire file unless we have to.
+bool IsBlacklistedBySha1sum(const FilePath& path) {
+ const struct BadEntry {
+ int64 size;
+ std::string sha1;
+ } bad_entries[] = {
+ // Flash 9 r31 - http://crbug.com/29237
+ { 7040080, "fa5803061125ca47846713b34a26a42f1f1e98bb" },
+ // Flash 9 r48 - http://crbug.com/29237
+ { 7040036, "0c4b3768a6d4bfba003088e4b9090d381de1af2b" },
+ };
+
+ int64 size;
+ if (!file_util::GetFileSize(path, &size))
+ return false;
+ for (size_t i = 0; i < ARRAYSIZE_UNSAFE(bad_entries); i++) {
+ if (bad_entries[i].size != size)
+ continue;
+
+ std::string file_content;
+ if (!file_util::ReadFileToString(path, &file_content))
+ continue;
+ std::string sha1 = base::SHA1HashString(file_content);
+ std::string sha1_readable;
+ for (size_t j = 0; j < sha1.size(); j++)
+ StringAppendF(&sha1_readable, "%02x", sha1[j] & 0xFF);
+ if (bad_entries[i].sha1 == sha1_readable)
+ return true;
+ }
+ return false;
+}
+
+// Some plugins are shells around other plugins; we prefer to use the
+// real plugin directly, if it's available. This function returns
+// true if we should prefer other plugins over this one. We'll still
+// use a "undesirable" plugin if no other option is available.
+bool IsUndesirablePlugin(const WebPluginInfo& info) {
+ std::string filename = info.path.BaseName().value();
+ const char* kUndesiredPlugins[] = {
+ "npcxoffice", // Crossover
+ "npwrapper", // nspluginwrapper
+ };
+ for (size_t i = 0; i < arraysize(kUndesiredPlugins); i++) {
+ if (filename.find(kUndesiredPlugins[i]) != std::string::npos) {
+ return true;
+ }
+ }
+ return false;
+}
+
+// Return true if we shouldn't load a plugin at all.
+// This is an ugly hack to blacklist Adobe Acrobat due to not supporting
+// its Xt-based mainloop.
+// http://code.google.com/p/chromium/issues/detail?id=38229
+// The gecko-mediaplayer plugins also crashes the entire browser sometimes.
+// http://code.google.com/p/chromium/issues/detail?id=24507
+bool IsBlacklistedPlugin(const FilePath& path) {
+ const char* kBlackListedPlugins[] = {
+ "nppdf.so", // Adobe PDF
+ "gecko-mediaplayer", // Gecko Media Player
+ };
+ std::string filename = path.BaseName().value();
+ for (size_t i = 0; i < arraysize(kBlackListedPlugins); i++) {
+ if (filename.find(kBlackListedPlugins[i]) != std::string::npos) {
+ return true;
+ }
+ }
+ return IsBlacklistedBySha1sum(path);
+}
+
+} // anonymous namespace
+
+namespace NPAPI {
+
+void PluginList::PlatformInit() {
+}
+
+void PluginList::GetPluginDirectories(std::vector<FilePath>* plugin_dirs) {
+ // See http://groups.google.com/group/chromium-dev/browse_thread/thread/7a70e5fcbac786a9
+ // for discussion.
+ // We first consult Chrome-specific dirs, then fall back on the logic
+ // Mozilla uses.
+
+ // Note: "extra" plugin dirs, including the Plugins subdirectory of
+ // your Chrome config, are examined before these. See the logic
+ // related to extra_plugin_dirs in plugin_list.cc.
+
+ // The Chrome binary dir + "plugins/".
+ FilePath dir;
+ PathService::Get(base::DIR_EXE, &dir);
+ plugin_dirs->push_back(dir.Append("plugins"));
+
+ // Mozilla code to reference:
+ // http://mxr.mozilla.org/firefox/ident?i=NS_APP_PLUGINS_DIR_LIST
+ // and tens of accompanying files (mxr is very helpful).
+ // This code carefully matches their behavior for compat reasons.
+
+ // 1) MOZ_PLUGIN_PATH env variable.
+ const char* moz_plugin_path = getenv("MOZ_PLUGIN_PATH");
+ if (moz_plugin_path) {
+ std::vector<std::string> paths;
+ SplitString(moz_plugin_path, ':', &paths);
+ for (size_t i = 0; i < paths.size(); ++i)
+ plugin_dirs->push_back(FilePath(paths[i]));
+ }
+
+ // 2) NS_USER_PLUGINS_DIR: ~/.mozilla/plugins.
+ // This is a de-facto standard, so even though we're not Mozilla, let's
+ // look in there too.
+ FilePath home = file_util::GetHomeDir();
+ if (!home.empty())
+ plugin_dirs->push_back(home.Append(".mozilla/plugins"));
+
+ // 3) NS_SYSTEM_PLUGINS_DIR:
+ // This varies across different browsers and versions, so check 'em all.
+ plugin_dirs->push_back(FilePath("/usr/lib/browser-plugins"));
+ plugin_dirs->push_back(FilePath("/usr/lib/mozilla/plugins"));
+ plugin_dirs->push_back(FilePath("/usr/lib/firefox/plugins"));
+ plugin_dirs->push_back(FilePath("/usr/lib/xulrunner-addons/plugins"));
+
+#if defined(ARCH_CPU_64_BITS)
+ // On my Ubuntu system, /usr/lib64 is a symlink to /usr/lib.
+ // But a user reported on their Fedora system they are separate.
+ plugin_dirs->push_back(FilePath("/usr/lib64/browser-plugins"));
+ plugin_dirs->push_back(FilePath("/usr/lib64/mozilla/plugins"));
+ plugin_dirs->push_back(FilePath("/usr/lib64/firefox/plugins"));
+ plugin_dirs->push_back(FilePath("/usr/lib64/xulrunner-addons/plugins"));
+#endif
+}
+
+void PluginList::LoadPluginsFromDir(const FilePath& dir_path,
+ std::vector<WebPluginInfo>* plugins,
+ std::set<FilePath>* visited_plugins) {
+ // See ScanPluginsDirectory near
+ // http://mxr.mozilla.org/firefox/source/modules/plugin/base/src/nsPluginHostImpl.cpp#5052
+
+ // Construct and stat a list of all filenames under consideration, for
+ // later sorting by mtime.
+ FileTimeList files;
+ file_util::FileEnumerator enumerator(dir_path,
+ false, // not recursive
+ file_util::FileEnumerator::FILES);
+ for (FilePath path = enumerator.Next(); !path.value().empty();
+ path = enumerator.Next()) {
+ // Skip over Mozilla .xpt files.
+ if (path.MatchesExtension(FILE_PATH_LITERAL(".xpt")))
+ continue;
+
+ // Java doesn't like being loaded through a symlink, since it uses
+ // its path to find dependent data files.
+ // file_util::AbsolutePath calls through to realpath(), which resolves
+ // symlinks.
+ FilePath orig_path = path;
+ file_util::AbsolutePath(&path);
+ LOG_IF(INFO, PluginList::DebugPluginLoading())
+ << "Resolved " << orig_path.value() << " -> " << path.value();
+
+ if (visited_plugins->find(path) != visited_plugins->end()) {
+ LOG_IF(INFO, PluginList::DebugPluginLoading())
+ << "Skipping duplicate instance of " << path.value();
+ continue;
+ }
+ visited_plugins->insert(path);
+
+ if (IsBlacklistedPlugin(path)) {
+ LOG_IF(INFO, PluginList::DebugPluginLoading())
+ << "Skipping blacklisted plugin " << path.value();
+ continue;
+ }
+
+ // Flash stops working if the containing directory involves 'netscape'.
+ // No joke. So use the other path if it's better.
+ static const char kFlashPlayerFilename[] = "libflashplayer.so";
+ static const char kNetscapeInPath[] = "/netscape/";
+ if (path.BaseName().value() == kFlashPlayerFilename &&
+ path.value().find(kNetscapeInPath) != std::string::npos) {
+ if (orig_path.value().find(kNetscapeInPath) == std::string::npos) {
+ // Go back to the old path.
+ path = orig_path;
+ } else {
+ LOG(ERROR) << "Flash misbehaves when used from a directory containing "
+ << kNetscapeInPath << ", so skipping " << orig_path.value();
+ continue;
+ }
+ }
+
+ // Get mtime.
+ file_util::FileInfo info;
+ if (!file_util::GetFileInfo(path, &info))
+ continue;
+
+ files.push_back(std::make_pair(path, info.last_modified));
+ }
+
+ // Sort the file list by time (and filename).
+ std::sort(files.begin(), files.end(), CompareTime);
+
+ // Load the files in order.
+ for (FileTimeList::const_iterator i = files.begin(); i != files.end(); ++i) {
+ LoadPlugin(i->first, plugins);
+ }
+}
+
+
+bool PluginList::ShouldLoadPlugin(const WebPluginInfo& info,
+ std::vector<WebPluginInfo>* plugins) {
+ LOG_IF(INFO, PluginList::DebugPluginLoading())
+ << "Considering " << info.path.value() << " (" << info.name << ")";
+
+ if (IsUndesirablePlugin(info)) {
+ LOG_IF(INFO, PluginList::DebugPluginLoading())
+ << info.path.value() << " is undesirable.";
+
+ // See if we have a better version of this plugin.
+ for (size_t i = 0; i < plugins->size(); ++i) {
+ if (plugins->at(i).name == info.name &&
+ !IsUndesirablePlugin(plugins->at(i))) {
+ // Skip the current undesirable one so we can use the better one
+ // we just found.
+ LOG_IF(INFO, PluginList::DebugPluginLoading())
+ << "Skipping " << info.path.value() << ", preferring "
+ << plugins->at(i).path.value();
+ return false;
+ }
+ }
+ }
+
+ // TODO(evanm): prefer the newest version of flash, etc. here?
+
+ LOG_IF(INFO, PluginList::DebugPluginLoading())
+ << "Using " << info.path.value();
+
+ return true;
+}
+
+} // namespace NPAPI