| 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 <tchar.h> | |
| 8 | |
| 9 #include <set> | |
| 10 | |
| 11 #include "base/basictypes.h" | |
| 12 #include "base/command_line.h" | |
| 13 #include "base/file_util.h" | |
| 14 #include "base/path_service.h" | |
| 15 #include "base/scoped_ptr.h" | |
| 16 #include "base/string_number_conversions.h" | |
| 17 #include "base/string_split.h" | |
| 18 #include "base/string_util.h" | |
| 19 #include "base/win/registry.h" | |
| 20 #include "webkit/glue/plugins/plugin_constants_win.h" | |
| 21 #include "webkit/glue/plugins/plugin_lib.h" | |
| 22 #include "webkit/glue/webkit_glue.h" | |
| 23 | |
| 24 namespace { | |
| 25 | |
| 26 const TCHAR kRegistryApps[] = | |
| 27 _T("Software\\Microsoft\\Windows\\CurrentVersion\\App Paths"); | |
| 28 const TCHAR kRegistryFirefox[] = _T("firefox.exe"); | |
| 29 const TCHAR kRegistryAcrobat[] = _T("Acrobat.exe"); | |
| 30 const TCHAR kRegistryAcrobatReader[] = _T("AcroRd32.exe"); | |
| 31 const TCHAR kRegistryWindowsMedia[] = _T("wmplayer.exe"); | |
| 32 const TCHAR kRegistryQuickTime[] = _T("QuickTimePlayer.exe"); | |
| 33 const TCHAR kRegistryPath[] = _T("Path"); | |
| 34 const TCHAR kRegistryFirefoxInstalled[] = | |
| 35 _T("SOFTWARE\\Mozilla\\Mozilla Firefox"); | |
| 36 const TCHAR kRegistryJava[] = | |
| 37 _T("Software\\JavaSoft\\Java Runtime Environment"); | |
| 38 const TCHAR kRegistryBrowserJavaVersion[] = _T("BrowserJavaVersion"); | |
| 39 const TCHAR kRegistryCurrentJavaVersion[] = _T("CurrentVersion"); | |
| 40 const TCHAR kRegistryJavaHome[] = _T("JavaHome"); | |
| 41 const TCHAR kJavaDeploy1[] = _T("npdeploytk.dll"); | |
| 42 const TCHAR kJavaDeploy2[] = _T("npdeployjava1.dll"); | |
| 43 | |
| 44 // The application path where we expect to find plugins. | |
| 45 void GetAppDirectory(std::set<FilePath>* plugin_dirs) { | |
| 46 FilePath app_path; | |
| 47 if (!webkit_glue::GetApplicationDirectory(&app_path)) | |
| 48 return; | |
| 49 | |
| 50 app_path = app_path.AppendASCII("plugins"); | |
| 51 plugin_dirs->insert(app_path); | |
| 52 } | |
| 53 | |
| 54 // The executable path where we expect to find plugins. | |
| 55 void GetExeDirectory(std::set<FilePath>* plugin_dirs) { | |
| 56 FilePath exe_path; | |
| 57 if (!webkit_glue::GetExeDirectory(&exe_path)) | |
| 58 return; | |
| 59 | |
| 60 exe_path = exe_path.AppendASCII("plugins"); | |
| 61 plugin_dirs->insert(exe_path); | |
| 62 } | |
| 63 | |
| 64 // Gets the installed path for a registered app. | |
| 65 bool GetInstalledPath(const TCHAR* app, FilePath* out) { | |
| 66 std::wstring reg_path(kRegistryApps); | |
| 67 reg_path.append(L"\\"); | |
| 68 reg_path.append(app); | |
| 69 | |
| 70 base::win::RegKey key(HKEY_LOCAL_MACHINE, reg_path.c_str(), KEY_READ); | |
| 71 std::wstring path; | |
| 72 if (key.ReadValue(kRegistryPath, &path)) { | |
| 73 *out = FilePath(path); | |
| 74 return true; | |
| 75 } | |
| 76 | |
| 77 return false; | |
| 78 } | |
| 79 | |
| 80 // Search the registry at the given path and detect plugin directories. | |
| 81 void GetPluginsInRegistryDirectory( | |
| 82 HKEY root_key, | |
| 83 const std::wstring& registry_folder, | |
| 84 std::set<FilePath>* plugin_dirs) { | |
| 85 for (base::win::RegistryKeyIterator iter(root_key, registry_folder.c_str()); | |
| 86 iter.Valid(); ++iter) { | |
| 87 // Use the registry to gather plugin across the file system. | |
| 88 std::wstring reg_path = registry_folder; | |
| 89 reg_path.append(L"\\"); | |
| 90 reg_path.append(iter.Name()); | |
| 91 base::win::RegKey key(root_key, reg_path.c_str(), KEY_READ); | |
| 92 | |
| 93 std::wstring path; | |
| 94 if (key.ReadValue(kRegistryPath, &path)) | |
| 95 plugin_dirs->insert(FilePath(path)); | |
| 96 } | |
| 97 } | |
| 98 | |
| 99 // Enumerate through the registry key to find all installed FireFox paths. | |
| 100 // FireFox 3 beta and version 2 can coexist. See bug: 1025003 | |
| 101 void GetFirefoxInstalledPaths(std::vector<FilePath>* out) { | |
| 102 base::win::RegistryKeyIterator it(HKEY_LOCAL_MACHINE, | |
| 103 kRegistryFirefoxInstalled); | |
| 104 for (; it.Valid(); ++it) { | |
| 105 std::wstring full_path = std::wstring(kRegistryFirefoxInstalled) + L"\\" + | |
| 106 it.Name() + L"\\Main"; | |
| 107 base::win::RegKey key(HKEY_LOCAL_MACHINE, full_path.c_str(), KEY_READ); | |
| 108 std::wstring install_dir; | |
| 109 if (!key.ReadValue(L"Install Directory", &install_dir)) | |
| 110 continue; | |
| 111 out->push_back(FilePath(install_dir)); | |
| 112 } | |
| 113 } | |
| 114 | |
| 115 // Get plugin directory locations from the Firefox install path. This is kind | |
| 116 // of a kludge, but it helps us locate the flash player for users that | |
| 117 // already have it for firefox. Not having to download yet-another-plugin | |
| 118 // is a good thing. | |
| 119 void GetFirefoxDirectory(std::set<FilePath>* plugin_dirs) { | |
| 120 std::vector<FilePath> paths; | |
| 121 GetFirefoxInstalledPaths(&paths); | |
| 122 for (unsigned int i = 0; i < paths.size(); ++i) { | |
| 123 plugin_dirs->insert(paths[i].Append(L"plugins")); | |
| 124 } | |
| 125 | |
| 126 FilePath firefox_app_data_plugin_path; | |
| 127 if (PathService::Get(base::DIR_APP_DATA, &firefox_app_data_plugin_path)) { | |
| 128 firefox_app_data_plugin_path = | |
| 129 firefox_app_data_plugin_path.AppendASCII("Mozilla") | |
| 130 .AppendASCII("plugins"); | |
| 131 plugin_dirs->insert(firefox_app_data_plugin_path); | |
| 132 } | |
| 133 } | |
| 134 | |
| 135 // Hardcoded logic to detect Acrobat plugins locations. | |
| 136 void GetAcrobatDirectory(std::set<FilePath>* plugin_dirs) { | |
| 137 FilePath path; | |
| 138 if (!GetInstalledPath(kRegistryAcrobatReader, &path) && | |
| 139 !GetInstalledPath(kRegistryAcrobat, &path)) { | |
| 140 return; | |
| 141 } | |
| 142 | |
| 143 plugin_dirs->insert(path.Append(L"Browser")); | |
| 144 } | |
| 145 | |
| 146 // Hardcoded logic to detect QuickTime plugin location. | |
| 147 void GetQuicktimeDirectory(std::set<FilePath>* plugin_dirs) { | |
| 148 FilePath path; | |
| 149 if (GetInstalledPath(kRegistryQuickTime, &path)) | |
| 150 plugin_dirs->insert(path.Append(L"plugins")); | |
| 151 } | |
| 152 | |
| 153 // Hardcoded logic to detect Windows Media Player plugin location. | |
| 154 void GetWindowsMediaDirectory(std::set<FilePath>* plugin_dirs) { | |
| 155 FilePath path; | |
| 156 if (GetInstalledPath(kRegistryWindowsMedia, &path)) | |
| 157 plugin_dirs->insert(path); | |
| 158 | |
| 159 // If the Windows Media Player Firefox plugin is installed before Firefox, | |
| 160 // the plugin will get written under PFiles\Plugins on one the drives | |
| 161 // (usually, but not always, the last letter). | |
| 162 int size = GetLogicalDriveStrings(0, NULL); | |
| 163 if (size) { | |
| 164 scoped_array<wchar_t> strings(new wchar_t[size]); | |
| 165 if (GetLogicalDriveStrings(size, strings.get())) { | |
| 166 wchar_t* next_drive = strings.get(); | |
| 167 while (*next_drive) { | |
| 168 if (GetDriveType(next_drive) == DRIVE_FIXED) { | |
| 169 FilePath pfiles(next_drive); | |
| 170 pfiles = pfiles.Append(L"PFiles\\Plugins"); | |
| 171 if (file_util::PathExists(pfiles)) | |
| 172 plugin_dirs->insert(pfiles); | |
| 173 } | |
| 174 next_drive = &next_drive[wcslen(next_drive) + 1]; | |
| 175 } | |
| 176 } | |
| 177 } | |
| 178 } | |
| 179 | |
| 180 // Hardcoded logic to detect Java plugin location. | |
| 181 void GetJavaDirectory(std::set<FilePath>* plugin_dirs) { | |
| 182 // Load the new NPAPI Java plugin | |
| 183 // 1. Open the main JRE key under HKLM | |
| 184 base::win::RegKey java_key(HKEY_LOCAL_MACHINE, kRegistryJava, | |
| 185 KEY_QUERY_VALUE); | |
| 186 | |
| 187 // 2. Read the current Java version | |
| 188 std::wstring java_version; | |
| 189 if (!java_key.ReadValue(kRegistryBrowserJavaVersion, &java_version)) | |
| 190 java_key.ReadValue(kRegistryCurrentJavaVersion, &java_version); | |
| 191 | |
| 192 if (!java_version.empty()) { | |
| 193 java_key.OpenKey(java_version.c_str(), KEY_QUERY_VALUE); | |
| 194 | |
| 195 // 3. Install path of the JRE binaries is specified in "JavaHome" | |
| 196 // value under the Java version key. | |
| 197 std::wstring java_plugin_directory; | |
| 198 if (java_key.ReadValue(kRegistryJavaHome, &java_plugin_directory)) { | |
| 199 // 4. The new plugin resides under the 'bin/new_plugin' | |
| 200 // subdirectory. | |
| 201 DCHECK(!java_plugin_directory.empty()); | |
| 202 java_plugin_directory.append(L"\\bin\\new_plugin"); | |
| 203 | |
| 204 // 5. We don't know the exact name of the DLL but it's in the form | |
| 205 // NP*.dll so just invoke LoadPlugins on this path. | |
| 206 plugin_dirs->insert(FilePath(java_plugin_directory)); | |
| 207 } | |
| 208 } | |
| 209 } | |
| 210 | |
| 211 } // anonymous namespace | |
| 212 | |
| 213 namespace NPAPI { | |
| 214 | |
| 215 void PluginList::PlatformInit() { | |
| 216 const CommandLine& command_line = *CommandLine::ForCurrentProcess(); | |
| 217 dont_load_new_wmp_ = command_line.HasSwitch(kUseOldWMPPluginSwitch); | |
| 218 } | |
| 219 | |
| 220 void PluginList::GetPluginDirectories(std::vector<FilePath>* plugin_dirs) { | |
| 221 // We use a set for uniqueness, which we require, over order, which we do not. | |
| 222 std::set<FilePath> dirs; | |
| 223 | |
| 224 // Load from the application-specific area | |
| 225 GetAppDirectory(&dirs); | |
| 226 | |
| 227 // Load from the executable area | |
| 228 GetExeDirectory(&dirs); | |
| 229 | |
| 230 // Load Java | |
| 231 GetJavaDirectory(&dirs); | |
| 232 | |
| 233 // Load firefox plugins too. This is mainly to try to locate | |
| 234 // a pre-installed Flash player. | |
| 235 GetFirefoxDirectory(&dirs); | |
| 236 | |
| 237 // Firefox hard-codes the paths of some popular plugins to ensure that | |
| 238 // the plugins are found. We are going to copy this as well. | |
| 239 GetAcrobatDirectory(&dirs); | |
| 240 GetQuicktimeDirectory(&dirs); | |
| 241 GetWindowsMediaDirectory(&dirs); | |
| 242 | |
| 243 for (std::set<FilePath>::iterator i = dirs.begin(); i != dirs.end(); ++i) | |
| 244 plugin_dirs->push_back(*i); | |
| 245 } | |
| 246 | |
| 247 void PluginList::LoadPluginsFromDir(const FilePath &path, | |
| 248 std::vector<WebPluginInfo>* plugins, | |
| 249 std::set<FilePath>* visited_plugins) { | |
| 250 WIN32_FIND_DATA find_file_data; | |
| 251 HANDLE find_handle; | |
| 252 | |
| 253 std::wstring dir = path.value(); | |
| 254 // FindFirstFile requires that you specify a wildcard for directories. | |
| 255 dir.append(L"\\NP*.DLL"); | |
| 256 | |
| 257 find_handle = FindFirstFile(dir.c_str(), &find_file_data); | |
| 258 if (find_handle == INVALID_HANDLE_VALUE) | |
| 259 return; | |
| 260 | |
| 261 do { | |
| 262 if (!(find_file_data.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)) { | |
| 263 FilePath filename = path.Append(find_file_data.cFileName); | |
| 264 LoadPlugin(filename, plugins); | |
| 265 visited_plugins->insert(filename); | |
| 266 } | |
| 267 } while (FindNextFile(find_handle, &find_file_data) != 0); | |
| 268 | |
| 269 DCHECK(GetLastError() == ERROR_NO_MORE_FILES); | |
| 270 FindClose(find_handle); | |
| 271 } | |
| 272 | |
| 273 void PluginList::LoadPluginsFromRegistry( | |
| 274 std::vector<WebPluginInfo>* plugins, | |
| 275 std::set<FilePath>* visited_plugins) { | |
| 276 std::set<FilePath> plugin_dirs; | |
| 277 | |
| 278 GetPluginsInRegistryDirectory( | |
| 279 HKEY_CURRENT_USER, kRegistryMozillaPlugins, &plugin_dirs); | |
| 280 GetPluginsInRegistryDirectory( | |
| 281 HKEY_LOCAL_MACHINE, kRegistryMozillaPlugins, &plugin_dirs); | |
| 282 | |
| 283 for (std::set<FilePath>::iterator i = plugin_dirs.begin(); | |
| 284 i != plugin_dirs.end(); ++i) { | |
| 285 LoadPlugin(*i, plugins); | |
| 286 visited_plugins->insert(*i); | |
| 287 } | |
| 288 } | |
| 289 | |
| 290 // Returns true if the given plugins share at least one mime type. This is used | |
| 291 // to differentiate newer versions of a plugin vs two plugins which happen to | |
| 292 // have the same filename. | |
| 293 bool HaveSharedMimeType(const WebPluginInfo& plugin1, | |
| 294 const WebPluginInfo& plugin2) { | |
| 295 for (size_t i = 0; i < plugin1.mime_types.size(); ++i) { | |
| 296 for (size_t j = 0; j < plugin2.mime_types.size(); ++j) { | |
| 297 if (plugin1.mime_types[i].mime_type == plugin2.mime_types[j].mime_type) | |
| 298 return true; | |
| 299 } | |
| 300 } | |
| 301 | |
| 302 return false; | |
| 303 } | |
| 304 | |
| 305 // Compares Windows style version strings (i.e. 1,2,3,4). Returns true if b's | |
| 306 // version is newer than a's, or false if it's equal or older. | |
| 307 bool IsNewerVersion(const std::wstring& a, const std::wstring& b) { | |
| 308 std::vector<std::wstring> a_ver, b_ver; | |
| 309 base::SplitString(a, ',', &a_ver); | |
| 310 base::SplitString(b, ',', &b_ver); | |
| 311 if (a_ver.size() == 1 && b_ver.size() == 1) { | |
| 312 a_ver.clear(); | |
| 313 b_ver.clear(); | |
| 314 base::SplitString(a, '.', &a_ver); | |
| 315 base::SplitString(b, '.', &b_ver); | |
| 316 } | |
| 317 if (a_ver.size() != b_ver.size()) | |
| 318 return false; | |
| 319 for (size_t i = 0; i < a_ver.size(); i++) { | |
| 320 int cur_a, cur_b; | |
| 321 base::StringToInt(a_ver[i], &cur_a); | |
| 322 base::StringToInt(b_ver[i], &cur_b); | |
| 323 | |
| 324 if (cur_a > cur_b) | |
| 325 return false; | |
| 326 if (cur_a < cur_b) | |
| 327 return true; | |
| 328 } | |
| 329 return false; | |
| 330 } | |
| 331 | |
| 332 bool PluginList::ShouldLoadPlugin(const WebPluginInfo& info, | |
| 333 std::vector<WebPluginInfo>* plugins) { | |
| 334 // Version check | |
| 335 | |
| 336 for (size_t i = 0; i < plugins->size(); ++i) { | |
| 337 std::wstring plugin1 = | |
| 338 StringToLowerASCII((*plugins)[i].path.BaseName().ToWStringHack()); | |
| 339 std::wstring plugin2 = | |
| 340 StringToLowerASCII(info.path.BaseName().ToWStringHack()); | |
| 341 if ((plugin1 == plugin2 && HaveSharedMimeType((*plugins)[i], info)) || | |
| 342 (plugin1 == kJavaDeploy1 && plugin2 == kJavaDeploy2) || | |
| 343 (plugin1 == kJavaDeploy2 && plugin2 == kJavaDeploy1)) { | |
| 344 if (!IsNewerVersion((*plugins)[i].version, info.version)) | |
| 345 return false; // We have loaded a plugin whose version is newer. | |
| 346 | |
| 347 plugins->erase(plugins->begin() + i); | |
| 348 break; | |
| 349 } | |
| 350 } | |
| 351 | |
| 352 // Troublemakers | |
| 353 | |
| 354 std::wstring filename = StringToLowerASCII(info.path.BaseName().value()); | |
| 355 // Depends on XPCOM. | |
| 356 if (filename == kMozillaActiveXPlugin) | |
| 357 return false; | |
| 358 | |
| 359 // Disable the Yahoo Application State plugin as it crashes the plugin | |
| 360 // process on return from NPObjectStub::OnInvoke. Please refer to | |
| 361 // http://b/issue?id=1372124 for more information. | |
| 362 if (filename == kYahooApplicationStatePlugin) | |
| 363 return false; | |
| 364 | |
| 365 // Disable the WangWang protocol handler plugin (npww.dll) as it crashes | |
| 366 // chrome during shutdown. Firefox also disables this plugin. | |
| 367 // Please refer to http://code.google.com/p/chromium/issues/detail?id=3953 | |
| 368 // for more information. | |
| 369 if (filename == kWanWangProtocolHandlerPlugin) | |
| 370 return false; | |
| 371 | |
| 372 // We only work with newer versions of the Java plugin which use NPAPI only | |
| 373 // and don't depend on XPCOM. | |
| 374 if (filename == kJavaPlugin1 || filename == kJavaPlugin2) { | |
| 375 std::vector<std::wstring> ver; | |
| 376 base::SplitString(info.version, '.', &ver); | |
| 377 int major, minor, update; | |
| 378 if (ver.size() == 4 && | |
| 379 base::StringToInt(ver[0], &major) && | |
| 380 base::StringToInt(ver[1], &minor) && | |
| 381 base::StringToInt(ver[2], &update)) { | |
| 382 if (major == 6 && minor == 0 && update < 120) | |
| 383 return false; // Java SE6 Update 11 or older. | |
| 384 } | |
| 385 } | |
| 386 | |
| 387 // Special WMP handling | |
| 388 | |
| 389 // If both the new and old WMP plugins exist, only load the new one. | |
| 390 if (filename == kNewWMPPlugin) { | |
| 391 if (dont_load_new_wmp_) | |
| 392 return false; | |
| 393 | |
| 394 for (size_t i = 0; i < plugins->size(); ++i) { | |
| 395 if ((*plugins)[i].path.BaseName().value() == kOldWMPPlugin) { | |
| 396 plugins->erase(plugins->begin() + i); | |
| 397 break; | |
| 398 } | |
| 399 } | |
| 400 } else if (filename == kOldWMPPlugin) { | |
| 401 for (size_t i = 0; i < plugins->size(); ++i) { | |
| 402 if ((*plugins)[i].path.BaseName().value() == kNewWMPPlugin) | |
| 403 return false; | |
| 404 } | |
| 405 } | |
| 406 | |
| 407 return true; | |
| 408 } | |
| 409 | |
| 410 } // namespace NPAPI | |
| OLD | NEW |