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 |