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