4404984e59362e548b9566c9a5599f7954bd6e3a
[WebKit-https.git] / WebCore / plugins / win / PluginDatabaseWin.cpp
1 /*
2  * Copyright (C) 2006, 2007 Apple Inc.  All rights reserved.
3  * Copyright (C) 2008 Collabora, Ltd.  All rights reserved.
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions
7  * are met:
8  * 1. Redistributions of source code must retain the above copyright
9  *    notice, this list of conditions and the following disclaimer.
10  * 2. Redistributions in binary form must reproduce the above copyright
11  *    notice, this list of conditions and the following disclaimer in the
12  *    documentation and/or other materials provided with the distribution.
13  *
14  * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY
15  * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
17  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL APPLE COMPUTER, INC. OR
18  * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
19  * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
20  * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
21  * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
22  * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
23  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
24  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 
25  */
26
27 #include "config.h"
28 #include "PluginDatabase.h"
29
30 #include "Frame.h"
31 #include "KURL.h"
32 #include "PluginPackage.h"
33 #include <windows.h>
34 #include <shlwapi.h>
35
36 namespace WebCore {
37
38 static inline void addPluginsFromRegistry(HKEY rootKey, PluginSet& plugins)
39 {
40     HKEY key;
41     HRESULT result = RegOpenKeyExW(rootKey, L"Software\\MozillaPlugins", 0, KEY_ENUMERATE_SUB_KEYS, &key);
42
43     if (result != ERROR_SUCCESS)
44         return;
45
46     wchar_t name[128];
47     FILETIME lastModified;
48
49     // Enumerate subkeys
50     for (int i = 0;; i++) {
51         DWORD nameLen = _countof(name);
52         result = RegEnumKeyExW(key, i, name, &nameLen, 0, 0, 0, &lastModified);
53
54         if (result != ERROR_SUCCESS)
55             break;
56
57         WCHAR pathStr[_MAX_PATH];
58         DWORD pathStrSize = sizeof(pathStr);
59         DWORD type;
60
61         result = SHGetValue(key, name, TEXT("Path"), &type, (LPBYTE)pathStr, &pathStrSize);
62         if (result != ERROR_SUCCESS || type != REG_SZ)
63             continue;
64
65         String path(pathStr, pathStrSize / sizeof(WCHAR) - 1);
66
67         time_t modifiedTime;
68         if (!getFileModificationTime(path, modifiedTime))
69             continue;
70
71         RefPtr<PluginPackage> package = PluginPackage::createPackage(path, modifiedTime);
72
73         if (package)
74             plugins.add(package);
75     }
76
77     RegCloseKey(key);
78 }
79
80 PluginSet PluginDatabase::getPluginsInPaths() const
81 {
82     // FIXME: This should be a case insensitive set.
83     HashSet<String> uniqueFilenames;
84     PluginSet plugins;
85
86     HANDLE hFind = INVALID_HANDLE_VALUE;
87     WIN32_FIND_DATAW findFileData;
88
89     RefPtr<PluginPackage> oldWMPPlugin;
90     RefPtr<PluginPackage> newWMPPlugin;
91
92     Vector<String>::const_iterator end = m_pluginPaths.end();
93     for (Vector<String>::const_iterator it = m_pluginPaths.begin(); it != end; ++it) {
94         String pattern = *it + "\\*";
95
96         hFind = FindFirstFileW(pattern.charactersWithNullTermination(), &findFileData);
97
98         if (hFind == INVALID_HANDLE_VALUE)
99             continue;
100
101         do {
102             if (findFileData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)
103                 continue;
104
105             String filename = String(findFileData.cFileName, wcslen(findFileData.cFileName));
106             if ((!filename.startsWith("np", false) || !filename.endsWith("dll", false)) &&
107                 (!equalIgnoringCase(filename, "Plugin.dll") || !it->endsWith("Shockwave 10", false)))
108                 continue;
109
110             String fullPath = *it + "\\" + filename;
111             if (!uniqueFilenames.add(fullPath).second)
112                 continue;
113
114             time_t modifiedTime;
115             if (!getFileModificationTime(fullPath, modifiedTime))
116                 continue;
117         
118             RefPtr<PluginPackage> pluginPackage = PluginPackage::createPackage(fullPath, modifiedTime);
119
120             if (pluginPackage) {
121                 plugins.add(pluginPackage);
122
123                 if (equalIgnoringCase(filename, "npdsplay.dll"))
124                     oldWMPPlugin = pluginPackage;
125                 else if (equalIgnoringCase(filename, "np-mswmp.dll"))
126                     newWMPPlugin = pluginPackage;
127             }
128
129         } while (FindNextFileW(hFind, &findFileData) != 0);
130
131         FindClose(hFind);
132     }
133
134     addPluginsFromRegistry(HKEY_LOCAL_MACHINE, plugins);
135     addPluginsFromRegistry(HKEY_CURRENT_USER, plugins);
136
137     // If both the old and new WMP plugin are present in the plugins set, 
138     // we remove the old one so we don't end up choosing the old one.
139     if (oldWMPPlugin && newWMPPlugin)
140         plugins.remove(oldWMPPlugin);
141
142     return plugins;
143 }
144
145 static inline Vector<int> parseVersionString(const String& versionString)
146 {
147     Vector<int> version;
148
149     unsigned startPos = 0;
150     unsigned endPos;
151     
152     while (startPos < versionString.length()) {
153         for (endPos = startPos; endPos < versionString.length(); ++endPos)
154             if (versionString[endPos] == '.' || versionString[endPos] == '_')
155                 break;
156
157         int versionComponent = versionString.substring(startPos, endPos - startPos).toInt();
158         version.append(versionComponent);
159
160         startPos = endPos + 1;
161     }
162
163     return version;
164 }
165
166 // This returns whether versionA is higher than versionB
167 static inline bool compareVersions(const Vector<int>& versionA, const Vector<int>& versionB)
168 {
169     for (unsigned i = 0; i < versionA.size(); i++) {
170         if (i >= versionB.size())
171             return true;
172
173         if (versionA[i] > versionB[i])
174             return true;
175         else if (versionA[i] < versionB[i])
176             return false;
177     }
178
179     // If we come here, the versions are either the same or versionB has an extra component, just return false
180     return false;
181 }
182
183 static inline void addMozillaPluginPaths(Vector<String>& paths)
184 {
185     // Enumerate all Mozilla plugin directories in the registry
186     HKEY key;
187     LONG result;
188     
189     result = RegOpenKeyEx(HKEY_LOCAL_MACHINE, TEXT("Software\\Mozilla"), 0, KEY_READ, &key);
190     if (result == ERROR_SUCCESS) {
191         WCHAR name[128];
192         FILETIME lastModified;
193
194         // Enumerate subkeys
195         for (int i = 0;; i++) {
196             DWORD nameLen = sizeof(name) / sizeof(WCHAR);
197             result = RegEnumKeyExW(key, i, name, &nameLen, 0, 0, 0, &lastModified);
198
199             if (result != ERROR_SUCCESS)
200                 break;
201
202             String extensionsPath = String(name, nameLen) + "\\Extensions";
203             HKEY extensionsKey;
204
205             // Try opening the key
206             result = RegOpenKeyEx(key, extensionsPath.charactersWithNullTermination(), 0, KEY_READ, &extensionsKey);
207
208             if (result == ERROR_SUCCESS) {
209                 // Now get the plugins path
210                 WCHAR pluginsPathStr[_MAX_PATH];
211                 DWORD pluginsPathSize = sizeof(pluginsPathStr);
212                 DWORD type;
213
214                 result = RegQueryValueEx(extensionsKey, TEXT("Plugins"), 0, &type, (LPBYTE)&pluginsPathStr, &pluginsPathSize);
215
216                 if (result == ERROR_SUCCESS && type == REG_SZ)
217                     paths.append(String(pluginsPathStr, pluginsPathSize / sizeof(WCHAR) - 1));
218
219                 RegCloseKey(extensionsKey);
220             }
221         }
222         
223         RegCloseKey(key);
224     }
225 }
226
227 static inline void addWindowsMediaPlayerPluginPath(Vector<String>& paths)
228 {
229     // The new WMP Firefox plugin is installed in \PFiles\Plugins if it can't find any Firefox installs
230     WCHAR pluginDirectoryStr[_MAX_PATH + 1];
231     DWORD pluginDirectorySize = ::ExpandEnvironmentStringsW(TEXT("%SYSTEMDRIVE%\\PFiles\\Plugins"), pluginDirectoryStr, _countof(pluginDirectoryStr));
232
233     if (pluginDirectorySize > 0 && pluginDirectorySize <= _countof(pluginDirectoryStr))
234         paths.append(String(pluginDirectoryStr, pluginDirectorySize - 1));
235
236     DWORD type;
237     WCHAR installationDirectoryStr[_MAX_PATH];
238     DWORD installationDirectorySize = sizeof(installationDirectoryStr);
239
240     HRESULT result = SHGetValue(HKEY_LOCAL_MACHINE, TEXT("Software\\Microsoft\\MediaPlayer"), TEXT("Installation Directory"), &type, (LPBYTE)&installationDirectoryStr, &installationDirectorySize);
241
242     if (result == ERROR_SUCCESS && type == REG_SZ)
243         paths.append(String(installationDirectoryStr, installationDirectorySize / sizeof(WCHAR) - 1));
244 }
245
246 static inline void addQuickTimePluginPath(Vector<String>& paths)
247 {
248     DWORD type;
249     WCHAR installationDirectoryStr[_MAX_PATH];
250     DWORD installationDirectorySize = sizeof(installationDirectoryStr);
251
252     HRESULT result = SHGetValue(HKEY_LOCAL_MACHINE, TEXT("Software\\Apple Computer, Inc.\\QuickTime"), TEXT("InstallDir"), &type, (LPBYTE)&installationDirectoryStr, &installationDirectorySize);
253
254     if (result == ERROR_SUCCESS && type == REG_SZ) {
255         String pluginDir = String(installationDirectoryStr, installationDirectorySize / sizeof(WCHAR) - 1) + "\\plugins";
256         paths.append(pluginDir);
257     }
258 }
259
260 static inline void addAdobeAcrobatPluginPath(Vector<String>& paths)
261 {
262     HKEY key;
263     HRESULT result = RegOpenKeyEx(HKEY_LOCAL_MACHINE, TEXT("Software\\Adobe\\Acrobat Reader"), 0, KEY_READ, &key);
264     if (result != ERROR_SUCCESS)
265         return;
266
267     WCHAR name[128];
268     FILETIME lastModified;
269
270     Vector<int> latestAcrobatVersion;
271     String latestAcrobatVersionString;
272
273     // Enumerate subkeys
274     for (int i = 0;; i++) {
275         DWORD nameLen = sizeof(name) / sizeof(WCHAR);
276         result = RegEnumKeyExW(key, i, name, &nameLen, 0, 0, 0, &lastModified);
277
278         if (result != ERROR_SUCCESS)
279             break;
280
281         Vector<int> acrobatVersion = parseVersionString(String(name, nameLen));
282         if (compareVersions(acrobatVersion, latestAcrobatVersion)) {
283             latestAcrobatVersion = acrobatVersion;
284             latestAcrobatVersionString = String(name, nameLen);
285         }
286     }
287
288     if (!latestAcrobatVersionString.isNull()) {
289         DWORD type;
290         WCHAR acrobatInstallPathStr[_MAX_PATH];
291         DWORD acrobatInstallPathSize = sizeof(acrobatInstallPathStr);
292
293         String acrobatPluginKeyPath = "Software\\Adobe\\Acrobat Reader\\" + latestAcrobatVersionString + "\\InstallPath";
294         result = SHGetValue(HKEY_LOCAL_MACHINE, acrobatPluginKeyPath.charactersWithNullTermination(), 0, &type, (LPBYTE)acrobatInstallPathStr, &acrobatInstallPathSize);
295
296         if (result == ERROR_SUCCESS) {
297             String acrobatPluginPath = String(acrobatInstallPathStr, acrobatInstallPathSize / sizeof(WCHAR) - 1) + "\\browser";
298             paths.append(acrobatPluginPath);
299         }
300     }
301
302     RegCloseKey(key);
303 }
304
305 static inline String safariPluginsPath()
306 {
307     WCHAR moduleFileNameStr[_MAX_PATH];
308     static String pluginsPath;
309     static bool cachedPluginPath = false;
310
311     if (!cachedPluginPath) {
312         cachedPluginPath = true;
313
314         int moduleFileNameLen = GetModuleFileName(0, moduleFileNameStr, _MAX_PATH);
315
316         if (!moduleFileNameLen || moduleFileNameLen == _MAX_PATH)
317             goto exit;
318
319         if (!PathRemoveFileSpec(moduleFileNameStr))
320             goto exit;
321
322         pluginsPath = String(moduleFileNameStr) + "\\Plugins";
323     }
324 exit:
325     return pluginsPath;
326 }
327
328 static inline void addMacromediaPluginPaths(Vector<String>& paths)
329 {
330     WCHAR systemDirectoryStr[MAX_PATH];
331
332     if (GetSystemDirectory(systemDirectoryStr, _countof(systemDirectoryStr)) == 0)
333         return;
334
335     WCHAR macromediaDirectoryStr[MAX_PATH];
336
337     PathCombine(macromediaDirectoryStr, systemDirectoryStr, TEXT("macromed\\Flash"));
338     paths.append(macromediaDirectoryStr);
339
340     PathCombine(macromediaDirectoryStr, systemDirectoryStr, TEXT("macromed\\Shockwave 10"));
341     paths.append(macromediaDirectoryStr);
342 }
343
344 Vector<String> PluginDatabase::defaultPluginPaths()
345 {
346     Vector<String> paths;
347     String ourPath = safariPluginsPath();
348
349     if (!ourPath.isNull())
350         paths.append(ourPath);
351     addQuickTimePluginPath(paths);
352     addAdobeAcrobatPluginPath(paths);
353     addMozillaPluginPaths(paths);
354     addWindowsMediaPlayerPluginPath(paths);
355     addMacromediaPluginPaths(paths);
356
357     return paths;
358 }
359
360 bool PluginDatabase::isPreferredPluginPath(const String& path)
361 {
362     String ourPath = safariPluginsPath();
363
364     if (!ourPath.isNull() && !path.isNull())
365         return ourPath == path;
366
367     return false;
368 }
369
370 }