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 |