| 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 |