| OLD | NEW |
| (Empty) |
| 1 // Copyright (c) 2010 The Chromium Authors. All rights reserved. | |
| 2 // Use of this source code is governed by a BSD-style license that can be | |
| 3 // found in the LICENSE file. | |
| 4 | |
| 5 #include "webkit/glue/plugins/plugin_list.h" | |
| 6 | |
| 7 #include "base/file_util.h" | |
| 8 #include "base/path_service.h" | |
| 9 #include "base/sha1.h" | |
| 10 #include "base/string_split.h" | |
| 11 #include "base/string_util.h" | |
| 12 #include "build/build_config.h" | |
| 13 | |
| 14 namespace { | |
| 15 | |
| 16 // We build up a list of files and mtimes so we can sort them. | |
| 17 typedef std::pair<FilePath, base::Time> FileAndTime; | |
| 18 typedef std::vector<FileAndTime> FileTimeList; | |
| 19 | |
| 20 // Comparator used to sort by descending mtime then ascending filename. | |
| 21 bool CompareTime(const FileAndTime& a, const FileAndTime& b) { | |
| 22 if (a.second == b.second) { | |
| 23 // Fall back on filename sorting, just to make the predicate valid. | |
| 24 return a.first < b.first; | |
| 25 } | |
| 26 | |
| 27 // Sort by mtime, descending. | |
| 28 return a.second > b.second; | |
| 29 } | |
| 30 | |
| 31 // Return true if |path| matches a known (file size, sha1sum) pair. | |
| 32 // The use of the file size is an optimization so we don't have to read in | |
| 33 // the entire file unless we have to. | |
| 34 bool IsBlacklistedBySha1sum(const FilePath& path) { | |
| 35 const struct BadEntry { | |
| 36 int64 size; | |
| 37 std::string sha1; | |
| 38 } bad_entries[] = { | |
| 39 // Flash 9 r31 - http://crbug.com/29237 | |
| 40 { 7040080, "fa5803061125ca47846713b34a26a42f1f1e98bb" }, | |
| 41 // Flash 9 r48 - http://crbug.com/29237 | |
| 42 { 7040036, "0c4b3768a6d4bfba003088e4b9090d381de1af2b" }, | |
| 43 }; | |
| 44 | |
| 45 int64 size; | |
| 46 if (!file_util::GetFileSize(path, &size)) | |
| 47 return false; | |
| 48 for (size_t i = 0; i < ARRAYSIZE_UNSAFE(bad_entries); i++) { | |
| 49 if (bad_entries[i].size != size) | |
| 50 continue; | |
| 51 | |
| 52 std::string file_content; | |
| 53 if (!file_util::ReadFileToString(path, &file_content)) | |
| 54 continue; | |
| 55 std::string sha1 = base::SHA1HashString(file_content); | |
| 56 std::string sha1_readable; | |
| 57 for (size_t j = 0; j < sha1.size(); j++) | |
| 58 base::StringAppendF(&sha1_readable, "%02x", sha1[j] & 0xFF); | |
| 59 if (bad_entries[i].sha1 == sha1_readable) | |
| 60 return true; | |
| 61 } | |
| 62 return false; | |
| 63 } | |
| 64 | |
| 65 // Some plugins are shells around other plugins; we prefer to use the | |
| 66 // real plugin directly, if it's available. This function returns | |
| 67 // true if we should prefer other plugins over this one. We'll still | |
| 68 // use a "undesirable" plugin if no other option is available. | |
| 69 bool IsUndesirablePlugin(const WebPluginInfo& info) { | |
| 70 std::string filename = info.path.BaseName().value(); | |
| 71 const char* kUndesiredPlugins[] = { | |
| 72 "npcxoffice", // Crossover | |
| 73 "npwrapper", // nspluginwrapper | |
| 74 }; | |
| 75 for (size_t i = 0; i < arraysize(kUndesiredPlugins); i++) { | |
| 76 if (filename.find(kUndesiredPlugins[i]) != std::string::npos) { | |
| 77 return true; | |
| 78 } | |
| 79 } | |
| 80 return false; | |
| 81 } | |
| 82 | |
| 83 // Return true if we shouldn't load a plugin at all. | |
| 84 // This is an ugly hack to blacklist Adobe Acrobat due to not supporting | |
| 85 // its Xt-based mainloop. | |
| 86 // http://code.google.com/p/chromium/issues/detail?id=38229 | |
| 87 // The gecko-mediaplayer plugins also crashes the entire browser sometimes. | |
| 88 // http://code.google.com/p/chromium/issues/detail?id=24507 | |
| 89 bool IsBlacklistedPlugin(const FilePath& path) { | |
| 90 const char* kBlackListedPlugins[] = { | |
| 91 "nppdf.so", // Adobe PDF | |
| 92 "gecko-mediaplayer", // Gecko Media Player | |
| 93 }; | |
| 94 std::string filename = path.BaseName().value(); | |
| 95 for (size_t i = 0; i < arraysize(kBlackListedPlugins); i++) { | |
| 96 if (filename.find(kBlackListedPlugins[i]) != std::string::npos) { | |
| 97 return true; | |
| 98 } | |
| 99 } | |
| 100 return IsBlacklistedBySha1sum(path); | |
| 101 } | |
| 102 | |
| 103 } // anonymous namespace | |
| 104 | |
| 105 namespace NPAPI { | |
| 106 | |
| 107 void PluginList::PlatformInit() { | |
| 108 } | |
| 109 | |
| 110 void PluginList::GetPluginDirectories(std::vector<FilePath>* plugin_dirs) { | |
| 111 // See http://groups.google.com/group/chromium-dev/browse_thread/thread/7a70e5
fcbac786a9 | |
| 112 // for discussion. | |
| 113 // We first consult Chrome-specific dirs, then fall back on the logic | |
| 114 // Mozilla uses. | |
| 115 | |
| 116 // Note: "extra" plugin dirs, including the Plugins subdirectory of | |
| 117 // your Chrome config, are examined before these. See the logic | |
| 118 // related to extra_plugin_dirs in plugin_list.cc. | |
| 119 | |
| 120 // The Chrome binary dir + "plugins/". | |
| 121 FilePath dir; | |
| 122 PathService::Get(base::DIR_EXE, &dir); | |
| 123 plugin_dirs->push_back(dir.Append("plugins")); | |
| 124 | |
| 125 // Chrome OS only loads plugins from /opt/google/chrome/plugins. | |
| 126 #if !defined(OS_CHROMEOS) | |
| 127 // Mozilla code to reference: | |
| 128 // http://mxr.mozilla.org/firefox/ident?i=NS_APP_PLUGINS_DIR_LIST | |
| 129 // and tens of accompanying files (mxr is very helpful). | |
| 130 // This code carefully matches their behavior for compat reasons. | |
| 131 | |
| 132 // 1) MOZ_PLUGIN_PATH env variable. | |
| 133 const char* moz_plugin_path = getenv("MOZ_PLUGIN_PATH"); | |
| 134 if (moz_plugin_path) { | |
| 135 std::vector<std::string> paths; | |
| 136 base::SplitString(moz_plugin_path, ':', &paths); | |
| 137 for (size_t i = 0; i < paths.size(); ++i) | |
| 138 plugin_dirs->push_back(FilePath(paths[i])); | |
| 139 } | |
| 140 | |
| 141 // 2) NS_USER_PLUGINS_DIR: ~/.mozilla/plugins. | |
| 142 // This is a de-facto standard, so even though we're not Mozilla, let's | |
| 143 // look in there too. | |
| 144 FilePath home = file_util::GetHomeDir(); | |
| 145 if (!home.empty()) | |
| 146 plugin_dirs->push_back(home.Append(".mozilla/plugins")); | |
| 147 | |
| 148 // 3) NS_SYSTEM_PLUGINS_DIR: | |
| 149 // This varies across different browsers and versions, so check 'em all. | |
| 150 plugin_dirs->push_back(FilePath("/usr/lib/browser-plugins")); | |
| 151 plugin_dirs->push_back(FilePath("/usr/lib/mozilla/plugins")); | |
| 152 plugin_dirs->push_back(FilePath("/usr/lib/firefox/plugins")); | |
| 153 plugin_dirs->push_back(FilePath("/usr/lib/xulrunner-addons/plugins")); | |
| 154 | |
| 155 #if defined(ARCH_CPU_64_BITS) | |
| 156 // On my Ubuntu system, /usr/lib64 is a symlink to /usr/lib. | |
| 157 // But a user reported on their Fedora system they are separate. | |
| 158 plugin_dirs->push_back(FilePath("/usr/lib64/browser-plugins")); | |
| 159 plugin_dirs->push_back(FilePath("/usr/lib64/mozilla/plugins")); | |
| 160 plugin_dirs->push_back(FilePath("/usr/lib64/firefox/plugins")); | |
| 161 plugin_dirs->push_back(FilePath("/usr/lib64/xulrunner-addons/plugins")); | |
| 162 #endif // defined(ARCH_CPU_64_BITS) | |
| 163 #endif // !defined(OS_CHROMEOS) | |
| 164 } | |
| 165 | |
| 166 void PluginList::LoadPluginsFromDir(const FilePath& dir_path, | |
| 167 std::vector<WebPluginInfo>* plugins, | |
| 168 std::set<FilePath>* visited_plugins) { | |
| 169 // See ScanPluginsDirectory near | |
| 170 // http://mxr.mozilla.org/firefox/source/modules/plugin/base/src/nsPluginHostI
mpl.cpp#5052 | |
| 171 | |
| 172 // Construct and stat a list of all filenames under consideration, for | |
| 173 // later sorting by mtime. | |
| 174 FileTimeList files; | |
| 175 file_util::FileEnumerator enumerator(dir_path, | |
| 176 false, // not recursive | |
| 177 file_util::FileEnumerator::FILES); | |
| 178 for (FilePath path = enumerator.Next(); !path.value().empty(); | |
| 179 path = enumerator.Next()) { | |
| 180 // Skip over Mozilla .xpt files. | |
| 181 if (path.MatchesExtension(FILE_PATH_LITERAL(".xpt"))) | |
| 182 continue; | |
| 183 | |
| 184 // Java doesn't like being loaded through a symlink, since it uses | |
| 185 // its path to find dependent data files. | |
| 186 // file_util::AbsolutePath calls through to realpath(), which resolves | |
| 187 // symlinks. | |
| 188 FilePath orig_path = path; | |
| 189 file_util::AbsolutePath(&path); | |
| 190 LOG_IF(ERROR, PluginList::DebugPluginLoading()) | |
| 191 << "Resolved " << orig_path.value() << " -> " << path.value(); | |
| 192 | |
| 193 if (visited_plugins->find(path) != visited_plugins->end()) { | |
| 194 LOG_IF(ERROR, PluginList::DebugPluginLoading()) | |
| 195 << "Skipping duplicate instance of " << path.value(); | |
| 196 continue; | |
| 197 } | |
| 198 visited_plugins->insert(path); | |
| 199 | |
| 200 if (IsBlacklistedPlugin(path)) { | |
| 201 LOG_IF(ERROR, PluginList::DebugPluginLoading()) | |
| 202 << "Skipping blacklisted plugin " << path.value(); | |
| 203 continue; | |
| 204 } | |
| 205 | |
| 206 // Flash stops working if the containing directory involves 'netscape'. | |
| 207 // No joke. So use the other path if it's better. | |
| 208 static const char kFlashPlayerFilename[] = "libflashplayer.so"; | |
| 209 static const char kNetscapeInPath[] = "/netscape/"; | |
| 210 if (path.BaseName().value() == kFlashPlayerFilename && | |
| 211 path.value().find(kNetscapeInPath) != std::string::npos) { | |
| 212 if (orig_path.value().find(kNetscapeInPath) == std::string::npos) { | |
| 213 // Go back to the old path. | |
| 214 path = orig_path; | |
| 215 } else { | |
| 216 LOG_IF(ERROR, PluginList::DebugPluginLoading()) | |
| 217 << "Flash misbehaves when used from a directory containing " | |
| 218 << kNetscapeInPath << ", so skipping " << orig_path.value(); | |
| 219 continue; | |
| 220 } | |
| 221 } | |
| 222 | |
| 223 // Get mtime. | |
| 224 base::PlatformFileInfo info; | |
| 225 if (!file_util::GetFileInfo(path, &info)) | |
| 226 continue; | |
| 227 | |
| 228 files.push_back(std::make_pair(path, info.last_modified)); | |
| 229 } | |
| 230 | |
| 231 // Sort the file list by time (and filename). | |
| 232 std::sort(files.begin(), files.end(), CompareTime); | |
| 233 | |
| 234 // Load the files in order. | |
| 235 for (FileTimeList::const_iterator i = files.begin(); i != files.end(); ++i) { | |
| 236 LoadPlugin(i->first, plugins); | |
| 237 } | |
| 238 } | |
| 239 | |
| 240 bool PluginList::ShouldLoadPlugin(const WebPluginInfo& info, | |
| 241 std::vector<WebPluginInfo>* plugins) { | |
| 242 LOG_IF(ERROR, PluginList::DebugPluginLoading()) | |
| 243 << "Considering " << info.path.value() << " (" << info.name << ")"; | |
| 244 | |
| 245 if (IsUndesirablePlugin(info)) { | |
| 246 LOG_IF(ERROR, PluginList::DebugPluginLoading()) | |
| 247 << info.path.value() << " is undesirable."; | |
| 248 | |
| 249 // See if we have a better version of this plugin. | |
| 250 for (size_t i = 0; i < plugins->size(); ++i) { | |
| 251 if (plugins->at(i).name == info.name && | |
| 252 !IsUndesirablePlugin(plugins->at(i))) { | |
| 253 // Skip the current undesirable one so we can use the better one | |
| 254 // we just found. | |
| 255 LOG_IF(ERROR, PluginList::DebugPluginLoading()) | |
| 256 << "Skipping " << info.path.value() << ", preferring " | |
| 257 << plugins->at(i).path.value(); | |
| 258 return false; | |
| 259 } | |
| 260 } | |
| 261 } | |
| 262 | |
| 263 // TODO(evanm): prefer the newest version of flash, etc. here? | |
| 264 | |
| 265 VLOG_IF(1, PluginList::DebugPluginLoading()) << "Using " << info.path.value(); | |
| 266 | |
| 267 return true; | |
| 268 } | |
| 269 | |
| 270 } // namespace NPAPI | |
| OLD | NEW |