OLD | NEW |
| (Empty) |
1 // Copyright (c) 2011 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/plugins/npapi/plugin_list.h" | |
6 | |
7 #include <algorithm> | |
8 #include <dlfcn.h> | |
9 #if defined(OS_OPENBSD) | |
10 #include <sys/exec_elf.h> | |
11 #else | |
12 #include <elf.h> | |
13 #endif | |
14 #include <fcntl.h> | |
15 #include <sys/stat.h> | |
16 #include <sys/types.h> | |
17 #include <unistd.h> | |
18 | |
19 #include "base/cpu.h" | |
20 #include "base/file_util.h" | |
21 #include "base/files/file_enumerator.h" | |
22 #include "base/native_library.h" | |
23 #include "base/path_service.h" | |
24 #include "base/posix/eintr_wrapper.h" | |
25 #include "base/sha1.h" | |
26 #include "base/strings/string_split.h" | |
27 #include "base/strings/string_util.h" | |
28 #include "base/strings/stringprintf.h" | |
29 #include "base/strings/sys_string_conversions.h" | |
30 #include "base/strings/utf_string_conversions.h" | |
31 #include "build/build_config.h" | |
32 #include "third_party/npapi/bindings/nphostapi.h" | |
33 | |
34 // These headers must be included in this order to make the declaration gods | |
35 // happy. | |
36 #include "base/third_party/nspr/prcpucfg_linux.h" | |
37 | |
38 namespace webkit { | |
39 namespace npapi { | |
40 | |
41 namespace { | |
42 | |
43 // We build up a list of files and mtimes so we can sort them. | |
44 typedef std::pair<base::FilePath, base::Time> FileAndTime; | |
45 typedef std::vector<FileAndTime> FileTimeList; | |
46 | |
47 enum PluginQuirk { | |
48 // No quirks - plugin is outright banned. | |
49 PLUGIN_QUIRK_NONE = 0, | |
50 // Plugin is using SSE2 instructions without checking for SSE2 instruction | |
51 // support. Ban the plugin if the system has no SSE2 support. | |
52 PLUGIN_QUIRK_MISSING_SSE2_CHECK = 1 << 0, | |
53 }; | |
54 | |
55 // Copied from nsplugindefs.h instead of including the file since it has a bunch | |
56 // of dependencies. | |
57 enum nsPluginVariable { | |
58 nsPluginVariable_NameString = 1, | |
59 nsPluginVariable_DescriptionString = 2 | |
60 }; | |
61 | |
62 // Comparator used to sort by descending mtime then ascending filename. | |
63 bool CompareTime(const FileAndTime& a, const FileAndTime& b) { | |
64 if (a.second == b.second) { | |
65 // Fall back on filename sorting, just to make the predicate valid. | |
66 return a.first < b.first; | |
67 } | |
68 | |
69 // Sort by mtime, descending. | |
70 return a.second > b.second; | |
71 } | |
72 | |
73 // Checks to see if the current environment meets any of the condtions set in | |
74 // |quirks|. Returns true if any of the conditions are met, or if |quirks| is | |
75 // PLUGIN_QUIRK_NONE. | |
76 bool CheckQuirks(PluginQuirk quirks) { | |
77 if (quirks == PLUGIN_QUIRK_NONE) | |
78 return true; | |
79 | |
80 if ((quirks & PLUGIN_QUIRK_MISSING_SSE2_CHECK) != 0) { | |
81 base::CPU cpu; | |
82 if (!cpu.has_sse2()) | |
83 return true; | |
84 } | |
85 | |
86 return false; | |
87 } | |
88 | |
89 // Return true if |path| matches a known (file size, sha1sum) pair. | |
90 // Also check against any PluginQuirks the bad plugin may have. | |
91 // The use of the file size is an optimization so we don't have to read in | |
92 // the entire file unless we have to. | |
93 bool IsBlacklistedBySha1sumAndQuirks(const base::FilePath& path) { | |
94 const struct BadEntry { | |
95 int64 size; | |
96 std::string sha1; | |
97 PluginQuirk quirks; | |
98 } bad_entries[] = { | |
99 // Flash 9 r31 - http://crbug.com/29237 | |
100 { 7040080, "fa5803061125ca47846713b34a26a42f1f1e98bb", PLUGIN_QUIRK_NONE }, | |
101 // Flash 9 r48 - http://crbug.com/29237 | |
102 { 7040036, "0c4b3768a6d4bfba003088e4b9090d381de1af2b", PLUGIN_QUIRK_NONE }, | |
103 // Flash 11.2.202.236, 32-bit - http://crbug.com/140086 | |
104 { 17406436, "1e07eac912faf9426c52a288c76c3b6238f90b6b", | |
105 PLUGIN_QUIRK_MISSING_SSE2_CHECK }, | |
106 // Flash 11.2.202.238, 32-bit - http://crbug.com/140086 | |
107 { 17410532, "e9401097e97c8443a7d9156be62184ffe1addd5c", | |
108 PLUGIN_QUIRK_MISSING_SSE2_CHECK }, | |
109 }; | |
110 | |
111 int64 size; | |
112 if (!file_util::GetFileSize(path, &size)) | |
113 return false; | |
114 for (size_t i = 0; i < ARRAYSIZE_UNSAFE(bad_entries); i++) { | |
115 if (bad_entries[i].size != size) | |
116 continue; | |
117 | |
118 std::string file_content; | |
119 if (!file_util::ReadFileToString(path, &file_content)) | |
120 continue; | |
121 std::string sha1 = base::SHA1HashString(file_content); | |
122 std::string sha1_readable; | |
123 for (size_t j = 0; j < sha1.size(); j++) | |
124 base::StringAppendF(&sha1_readable, "%02x", sha1[j] & 0xFF); | |
125 if (bad_entries[i].sha1 == sha1_readable) | |
126 return CheckQuirks(bad_entries[i].quirks); | |
127 } | |
128 return false; | |
129 } | |
130 | |
131 // Some plugins are shells around other plugins; we prefer to use the | |
132 // real plugin directly, if it's available. This function returns | |
133 // true if we should prefer other plugins over this one. We'll still | |
134 // use a "undesirable" plugin if no other option is available. | |
135 bool IsUndesirablePlugin(const WebPluginInfo& info) { | |
136 std::string filename = info.path.BaseName().value(); | |
137 const char* kUndesiredPlugins[] = { | |
138 "npcxoffice", // Crossover | |
139 "npwrapper", // nspluginwrapper | |
140 }; | |
141 for (size_t i = 0; i < arraysize(kUndesiredPlugins); i++) { | |
142 if (filename.find(kUndesiredPlugins[i]) != std::string::npos) { | |
143 return true; | |
144 } | |
145 } | |
146 return false; | |
147 } | |
148 | |
149 // Return true if we shouldn't load a plugin at all. | |
150 // This is an ugly hack to blacklist Adobe Acrobat due to not supporting | |
151 // its Xt-based mainloop. | |
152 // http://code.google.com/p/chromium/issues/detail?id=38229 | |
153 bool IsBlacklistedPlugin(const base::FilePath& path) { | |
154 const char* kBlackListedPlugins[] = { | |
155 "nppdf.so", // Adobe PDF | |
156 }; | |
157 std::string filename = path.BaseName().value(); | |
158 for (size_t i = 0; i < arraysize(kBlackListedPlugins); i++) { | |
159 if (filename.find(kBlackListedPlugins[i]) != std::string::npos) { | |
160 return true; | |
161 } | |
162 } | |
163 return IsBlacklistedBySha1sumAndQuirks(path); | |
164 } | |
165 | |
166 // Read the ELF header and return true if it is usable on | |
167 // the current architecture (e.g. 32-bit ELF on 32-bit build). | |
168 // Returns false on other errors as well. | |
169 bool ELFMatchesCurrentArchitecture(const base::FilePath& filename) { | |
170 // First make sure we can open the file and it is in fact, a regular file. | |
171 struct stat stat_buf; | |
172 // Open with O_NONBLOCK so we don't block on pipes. | |
173 int fd = open(filename.value().c_str(), O_RDONLY|O_NONBLOCK); | |
174 if (fd < 0) | |
175 return false; | |
176 bool ret = (fstat(fd, &stat_buf) >= 0 && S_ISREG(stat_buf.st_mode)); | |
177 if (HANDLE_EINTR(close(fd)) < 0) | |
178 return false; | |
179 if (!ret) | |
180 return false; | |
181 | |
182 const size_t kELFBufferSize = 5; | |
183 char buffer[kELFBufferSize]; | |
184 if (!file_util::ReadFile(filename, buffer, kELFBufferSize)) | |
185 return false; | |
186 | |
187 if (buffer[0] != ELFMAG0 || | |
188 buffer[1] != ELFMAG1 || | |
189 buffer[2] != ELFMAG2 || | |
190 buffer[3] != ELFMAG3) { | |
191 // Not an ELF file, perhaps? | |
192 return false; | |
193 } | |
194 | |
195 int elf_class = buffer[EI_CLASS]; | |
196 #if defined(ARCH_CPU_32_BITS) | |
197 if (elf_class == ELFCLASS32) | |
198 return true; | |
199 #elif defined(ARCH_CPU_64_BITS) | |
200 if (elf_class == ELFCLASS64) | |
201 return true; | |
202 #endif | |
203 | |
204 return false; | |
205 } | |
206 | |
207 // This structure matches enough of nspluginwrapper's NPW_PluginInfo | |
208 // for us to extract the real plugin path. | |
209 struct __attribute__((packed)) NSPluginWrapperInfo { | |
210 char ident[32]; // NSPluginWrapper magic identifier (includes version). | |
211 char path[PATH_MAX]; // Path to wrapped plugin. | |
212 }; | |
213 | |
214 // Test a plugin for whether it's been wrapped by NSPluginWrapper, and | |
215 // if so attempt to unwrap it. Pass in an opened plugin handle; on | |
216 // success, |dl| and |unwrapped_path| will be filled in with the newly | |
217 // opened plugin. On failure, params are left unmodified. | |
218 void UnwrapNSPluginWrapper(void **dl, base::FilePath* unwrapped_path) { | |
219 NSPluginWrapperInfo* info = | |
220 reinterpret_cast<NSPluginWrapperInfo*>(dlsym(*dl, "NPW_Plugin")); | |
221 if (!info) | |
222 return; // Not a NSPW plugin. | |
223 | |
224 // Here we could check the NSPW ident field for the versioning | |
225 // information, but the path field is available in all versions | |
226 // anyway. | |
227 | |
228 // Grab the path to the wrapped plugin. Just in case the structure | |
229 // format changes, protect against the path not being null-terminated. | |
230 char* path_end = static_cast<char*>(memchr(info->path, '\0', | |
231 sizeof(info->path))); | |
232 if (!path_end) | |
233 path_end = info->path + sizeof(info->path); | |
234 base::FilePath path = base::FilePath( | |
235 std::string(info->path, path_end - info->path)); | |
236 | |
237 if (!ELFMatchesCurrentArchitecture(path)) { | |
238 LOG(WARNING) << path.value() << " is nspluginwrapper wrapping a " | |
239 << "plugin for a different architecture; it will " | |
240 << "work better if you instead use a native plugin."; | |
241 return; | |
242 } | |
243 | |
244 std::string error; | |
245 void* newdl = base::LoadNativeLibrary(path, &error); | |
246 if (!newdl) { | |
247 // We couldn't load the unwrapped plugin for some reason, despite | |
248 // being able to load the wrapped one. Just use the wrapped one. | |
249 LOG_IF(ERROR, PluginList::DebugPluginLoading()) | |
250 << "Could not use unwrapped nspluginwrapper plugin " | |
251 << unwrapped_path->value() << " (" << error << "), " | |
252 << "using the wrapped one."; | |
253 return; | |
254 } | |
255 | |
256 // Unload the wrapped plugin, and use the wrapped plugin instead. | |
257 LOG_IF(ERROR, PluginList::DebugPluginLoading()) | |
258 << "Using unwrapped version " << unwrapped_path->value() | |
259 << " of nspluginwrapper-wrapped plugin."; | |
260 base::UnloadNativeLibrary(*dl); | |
261 *dl = newdl; | |
262 *unwrapped_path = path; | |
263 } | |
264 | |
265 } // namespace | |
266 | |
267 bool PluginList::ReadWebPluginInfo(const base::FilePath& filename, | |
268 WebPluginInfo* info) { | |
269 // The file to reference is: | |
270 // http://mxr.mozilla.org/firefox/source/modules/plugin/base/src/nsPluginsDirU
nix.cpp | |
271 | |
272 // Skip files that aren't appropriate for our architecture. | |
273 if (!ELFMatchesCurrentArchitecture(filename)) { | |
274 LOG_IF(ERROR, PluginList::DebugPluginLoading()) | |
275 << "Skipping plugin " << filename.value() | |
276 << " because it doesn't match the current architecture."; | |
277 return false; | |
278 } | |
279 | |
280 std::string error; | |
281 void* dl = base::LoadNativeLibrary(filename, &error); | |
282 if (!dl) { | |
283 LOG_IF(ERROR, PluginList::DebugPluginLoading()) | |
284 << "While reading plugin info, unable to load library " | |
285 << filename.value() << " (" << error << "), skipping."; | |
286 return false; | |
287 } | |
288 | |
289 info->path = filename; | |
290 | |
291 // Attempt to swap in the wrapped plugin if this is nspluginwrapper. | |
292 UnwrapNSPluginWrapper(&dl, &info->path); | |
293 | |
294 // See comments in plugin_lib_mac regarding this symbol. | |
295 typedef const char* (*NP_GetMimeDescriptionType)(); | |
296 NP_GetMimeDescriptionType NP_GetMIMEDescription = | |
297 reinterpret_cast<NP_GetMimeDescriptionType>( | |
298 dlsym(dl, "NP_GetMIMEDescription")); | |
299 const char* mime_description = NULL; | |
300 if (!NP_GetMIMEDescription) { | |
301 LOG_IF(ERROR, PluginList::DebugPluginLoading()) | |
302 << "Plugin " << filename.value() << " doesn't have a " | |
303 << "NP_GetMIMEDescription symbol"; | |
304 return false; | |
305 } | |
306 mime_description = NP_GetMIMEDescription(); | |
307 | |
308 if (!mime_description) { | |
309 LOG_IF(ERROR, PluginList::DebugPluginLoading()) | |
310 << "MIME description for " << filename.value() << " is empty"; | |
311 return false; | |
312 } | |
313 ParseMIMEDescription(mime_description, &info->mime_types); | |
314 | |
315 // The plugin name and description live behind NP_GetValue calls. | |
316 typedef NPError (*NP_GetValueType)(void* unused, | |
317 nsPluginVariable variable, | |
318 void* value_out); | |
319 NP_GetValueType NP_GetValue = | |
320 reinterpret_cast<NP_GetValueType>(dlsym(dl, "NP_GetValue")); | |
321 if (NP_GetValue) { | |
322 const char* name = NULL; | |
323 NP_GetValue(NULL, nsPluginVariable_NameString, &name); | |
324 if (name) { | |
325 info->name = UTF8ToUTF16(name); | |
326 ExtractVersionString(name, info); | |
327 } | |
328 | |
329 const char* description = NULL; | |
330 NP_GetValue(NULL, nsPluginVariable_DescriptionString, &description); | |
331 if (description) { | |
332 info->desc = UTF8ToUTF16(description); | |
333 if (info->version.empty()) | |
334 ExtractVersionString(description, info); | |
335 } | |
336 | |
337 LOG_IF(ERROR, PluginList::DebugPluginLoading()) | |
338 << "Got info for plugin " << filename.value() | |
339 << " Name = \"" << UTF16ToUTF8(info->name) | |
340 << "\", Description = \"" << UTF16ToUTF8(info->desc) | |
341 << "\", Version = \"" << UTF16ToUTF8(info->version) | |
342 << "\"."; | |
343 } else { | |
344 LOG_IF(ERROR, PluginList::DebugPluginLoading()) | |
345 << "Plugin " << filename.value() | |
346 << " has no GetValue() and probably won't work."; | |
347 } | |
348 | |
349 // Intentionally not unloading the plugin here, it can lead to crashes. | |
350 | |
351 return true; | |
352 } | |
353 | |
354 // static | |
355 void PluginList::ParseMIMEDescription( | |
356 const std::string& description, | |
357 std::vector<WebPluginMimeType>* mime_types) { | |
358 // We parse the description here into WebPluginMimeType structures. | |
359 // Naively from the NPAPI docs you'd think you could use | |
360 // string-splitting, but the Firefox parser turns out to do something | |
361 // different: find the first colon, then the second, then a semi. | |
362 // | |
363 // See ParsePluginMimeDescription near | |
364 // http://mxr.mozilla.org/firefox/source/modules/plugin/base/src/nsPluginsDirU
tils.h#53 | |
365 | |
366 std::string::size_type ofs = 0; | |
367 for (;;) { | |
368 WebPluginMimeType mime_type; | |
369 | |
370 std::string::size_type end = description.find(':', ofs); | |
371 if (end == std::string::npos) | |
372 break; | |
373 mime_type.mime_type = description.substr(ofs, end - ofs); | |
374 ofs = end + 1; | |
375 | |
376 end = description.find(':', ofs); | |
377 if (end == std::string::npos) | |
378 break; | |
379 const std::string extensions = description.substr(ofs, end - ofs); | |
380 base::SplitString(extensions, ',', &mime_type.file_extensions); | |
381 ofs = end + 1; | |
382 | |
383 end = description.find(';', ofs); | |
384 // It's ok for end to run off the string here. If there's no | |
385 // trailing semicolon we consume the remainder of the string. | |
386 if (end != std::string::npos) { | |
387 mime_type.description = UTF8ToUTF16(description.substr(ofs, end - ofs)); | |
388 } else { | |
389 mime_type.description = UTF8ToUTF16(description.substr(ofs)); | |
390 } | |
391 mime_types->push_back(mime_type); | |
392 if (end == std::string::npos) | |
393 break; | |
394 ofs = end + 1; | |
395 } | |
396 } | |
397 | |
398 // static | |
399 void PluginList::ExtractVersionString(const std::string& desc, | |
400 WebPluginInfo* info) { | |
401 // This matching works by extracting a version substring, along the lines of: | |
402 // No postfix: second match in .*<prefix>.*$ | |
403 // With postfix: second match .*<prefix>.*<postfix> | |
404 static const struct { | |
405 const char* kPrefix; | |
406 const char* kPostfix; | |
407 } kPrePostFixes[] = { | |
408 { "Shockwave Flash ", 0 }, | |
409 { "Java(TM) Plug-in ", 0 }, | |
410 { "(using IcedTea-Web ", " " }, | |
411 { 0, 0 } | |
412 }; | |
413 std::string version; | |
414 for (size_t i = 0; kPrePostFixes[i].kPrefix; ++i) { | |
415 size_t pos; | |
416 if ((pos = desc.find(kPrePostFixes[i].kPrefix)) != std::string::npos) { | |
417 version = desc.substr(pos + strlen(kPrePostFixes[i].kPrefix)); | |
418 pos = std::string::npos; | |
419 if (kPrePostFixes[i].kPostfix) | |
420 pos = version.find(kPrePostFixes[i].kPostfix); | |
421 if (pos != std::string::npos) | |
422 version = version.substr(0, pos); | |
423 break; | |
424 } | |
425 } | |
426 if (!version.empty()) { | |
427 info->version = UTF8ToUTF16(version); | |
428 } | |
429 } | |
430 | |
431 void PluginList::GetPluginDirectories(std::vector<base::FilePath>* plugin_dirs)
{ | |
432 // See http://groups.google.com/group/chromium-dev/browse_thread/thread/7a70e5
fcbac786a9 | |
433 // for discussion. | |
434 // We first consult Chrome-specific dirs, then fall back on the logic | |
435 // Mozilla uses. | |
436 | |
437 if (PluginList::plugins_discovery_disabled_) | |
438 return; | |
439 | |
440 // Note: "extra" plugin dirs and paths are examined before these. | |
441 // "Extra" are those added by PluginList::AddExtraPluginDir() and | |
442 // PluginList::AddExtraPluginPath(). | |
443 | |
444 // The Chrome binary dir + "plugins/". | |
445 base::FilePath dir; | |
446 PathService::Get(base::DIR_EXE, &dir); | |
447 plugin_dirs->push_back(dir.Append("plugins")); | |
448 | |
449 // Chrome OS only loads plugins from /opt/google/chrome/plugins. | |
450 #if !defined(OS_CHROMEOS) | |
451 // Mozilla code to reference: | |
452 // http://mxr.mozilla.org/firefox/ident?i=NS_APP_PLUGINS_DIR_LIST | |
453 // and tens of accompanying files (mxr is very helpful). | |
454 // This code carefully matches their behavior for compat reasons. | |
455 | |
456 // 1) MOZ_PLUGIN_PATH env variable. | |
457 const char* moz_plugin_path = getenv("MOZ_PLUGIN_PATH"); | |
458 if (moz_plugin_path) { | |
459 std::vector<std::string> paths; | |
460 base::SplitString(moz_plugin_path, ':', &paths); | |
461 for (size_t i = 0; i < paths.size(); ++i) | |
462 plugin_dirs->push_back(base::FilePath(paths[i])); | |
463 } | |
464 | |
465 // 2) NS_USER_PLUGINS_DIR: ~/.mozilla/plugins. | |
466 // This is a de-facto standard, so even though we're not Mozilla, let's | |
467 // look in there too. | |
468 base::FilePath home = file_util::GetHomeDir(); | |
469 if (!home.empty()) | |
470 plugin_dirs->push_back(home.Append(".mozilla/plugins")); | |
471 | |
472 // 3) NS_SYSTEM_PLUGINS_DIR: | |
473 // This varies across different browsers and versions, so check 'em all. | |
474 plugin_dirs->push_back(base::FilePath("/usr/lib/browser-plugins")); | |
475 plugin_dirs->push_back(base::FilePath("/usr/lib/mozilla/plugins")); | |
476 plugin_dirs->push_back(base::FilePath("/usr/lib/firefox/plugins")); | |
477 plugin_dirs->push_back(base::FilePath("/usr/lib/xulrunner-addons/plugins")); | |
478 | |
479 #if defined(ARCH_CPU_64_BITS) | |
480 // On my Ubuntu system, /usr/lib64 is a symlink to /usr/lib. | |
481 // But a user reported on their Fedora system they are separate. | |
482 plugin_dirs->push_back(base::FilePath("/usr/lib64/browser-plugins")); | |
483 plugin_dirs->push_back(base::FilePath("/usr/lib64/mozilla/plugins")); | |
484 plugin_dirs->push_back(base::FilePath("/usr/lib64/firefox/plugins")); | |
485 plugin_dirs->push_back(base::FilePath("/usr/lib64/xulrunner-addons/plugins")); | |
486 #endif // defined(ARCH_CPU_64_BITS) | |
487 #endif // !defined(OS_CHROMEOS) | |
488 } | |
489 | |
490 void PluginList::GetPluginsInDir( | |
491 const base::FilePath& dir_path, std::vector<base::FilePath>* plugins) { | |
492 // See ScanPluginsDirectory near | |
493 // http://mxr.mozilla.org/firefox/source/modules/plugin/base/src/nsPluginHostI
mpl.cpp#5052 | |
494 | |
495 // Construct and stat a list of all filenames under consideration, for | |
496 // later sorting by mtime. | |
497 FileTimeList files; | |
498 base::FileEnumerator enumerator(dir_path, | |
499 false, // not recursive | |
500 base::FileEnumerator::FILES); | |
501 for (base::FilePath path = enumerator.Next(); !path.value().empty(); | |
502 path = enumerator.Next()) { | |
503 // Skip over Mozilla .xpt files. | |
504 if (path.MatchesExtension(FILE_PATH_LITERAL(".xpt"))) | |
505 continue; | |
506 | |
507 // Java doesn't like being loaded through a symlink, since it uses | |
508 // its path to find dependent data files. | |
509 // MakeAbsoluteFilePath calls through to realpath(), which resolves | |
510 // symlinks. | |
511 base::FilePath orig_path = path; | |
512 path = base::MakeAbsoluteFilePath(path); | |
513 if (path.empty()) | |
514 path = orig_path; | |
515 LOG_IF(ERROR, PluginList::DebugPluginLoading()) | |
516 << "Resolved " << orig_path.value() << " -> " << path.value(); | |
517 | |
518 if (std::find(plugins->begin(), plugins->end(), path) != plugins->end()) { | |
519 LOG_IF(ERROR, PluginList::DebugPluginLoading()) | |
520 << "Skipping duplicate instance of " << path.value(); | |
521 continue; | |
522 } | |
523 | |
524 if (IsBlacklistedPlugin(path)) { | |
525 LOG_IF(ERROR, PluginList::DebugPluginLoading()) | |
526 << "Skipping blacklisted plugin " << path.value(); | |
527 continue; | |
528 } | |
529 | |
530 // Flash stops working if the containing directory involves 'netscape'. | |
531 // No joke. So use the other path if it's better. | |
532 static const char kFlashPlayerFilename[] = "libflashplayer.so"; | |
533 static const char kNetscapeInPath[] = "/netscape/"; | |
534 if (path.BaseName().value() == kFlashPlayerFilename && | |
535 path.value().find(kNetscapeInPath) != std::string::npos) { | |
536 if (orig_path.value().find(kNetscapeInPath) == std::string::npos) { | |
537 // Go back to the old path. | |
538 path = orig_path; | |
539 } else { | |
540 LOG_IF(ERROR, PluginList::DebugPluginLoading()) | |
541 << "Flash misbehaves when used from a directory containing " | |
542 << kNetscapeInPath << ", so skipping " << orig_path.value(); | |
543 continue; | |
544 } | |
545 } | |
546 | |
547 // Get mtime. | |
548 base::PlatformFileInfo info; | |
549 if (!file_util::GetFileInfo(path, &info)) | |
550 continue; | |
551 | |
552 files.push_back(std::make_pair(path, info.last_modified)); | |
553 } | |
554 | |
555 // Sort the file list by time (and filename). | |
556 std::sort(files.begin(), files.end(), CompareTime); | |
557 | |
558 // Load the files in order. | |
559 for (FileTimeList::const_iterator i = files.begin(); i != files.end(); ++i) { | |
560 plugins->push_back(i->first); | |
561 } | |
562 } | |
563 | |
564 bool PluginList::ShouldLoadPluginUsingPluginList( | |
565 const WebPluginInfo& info, std::vector<webkit::WebPluginInfo>* plugins) { | |
566 LOG_IF(ERROR, PluginList::DebugPluginLoading()) | |
567 << "Considering " << info.path.value() << " (" << info.name << ")"; | |
568 | |
569 if (IsUndesirablePlugin(info)) { | |
570 LOG_IF(ERROR, PluginList::DebugPluginLoading()) | |
571 << info.path.value() << " is undesirable."; | |
572 | |
573 // See if we have a better version of this plugin. | |
574 for (size_t j = 0; j < plugins->size(); ++j) { | |
575 if ((*plugins)[j].name == info.name && | |
576 !IsUndesirablePlugin((*plugins)[j])) { | |
577 // Skip the current undesirable one so we can use the better one | |
578 // we just found. | |
579 LOG_IF(ERROR, PluginList::DebugPluginLoading()) | |
580 << "Skipping " << info.path.value() << ", preferring " | |
581 << (*plugins)[j].path.value(); | |
582 return false; | |
583 } | |
584 } | |
585 } | |
586 | |
587 // TODO(evanm): prefer the newest version of flash, etc. here? | |
588 | |
589 VLOG_IF(1, PluginList::DebugPluginLoading()) << "Using " << info.path.value(); | |
590 | |
591 return true; | |
592 } | |
593 | |
594 } // namespace npapi | |
595 } // namespace webkit | |
OLD | NEW |