2 * Copyright (C) 2010 Apple Inc. All rights reserved.
4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions
7 * 1. Redistributions of source code must retain the above copyright
8 * notice, this list of conditions and the following disclaimer.
9 * 2. Redistributions in binary form must reproduce the above copyright
10 * notice, this list of conditions and the following disclaimer in the
11 * documentation and/or other materials provided with the distribution.
13 * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
14 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
15 * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
16 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
17 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
18 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
19 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
20 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
21 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
22 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
23 * THE POSSIBILITY OF SUCH DAMAGE.
27 #include "PluginInfoStore.h"
29 #include "NetscapePluginModule.h"
30 #include <WebCore/FileSystem.h>
31 #include <WebCore/PathWalker.h>
34 using namespace WebCore;
38 static inline Vector<int> parseVersionString(const String& versionString)
42 unsigned startPos = 0;
45 while (startPos < versionString.length()) {
46 for (endPos = startPos; endPos < versionString.length(); ++endPos)
47 if (versionString[endPos] == '.' || versionString[endPos] == '_')
50 int versionComponent = versionString.substring(startPos, endPos - startPos).toInt();
51 version.append(versionComponent);
53 startPos = endPos + 1;
59 // This returns whether versionA is higher than versionB
60 static inline bool compareVersions(const Vector<int>& versionA, const Vector<int>& versionB)
62 for (unsigned i = 0; i < versionA.size(); i++) {
63 if (i >= versionB.size())
66 if (versionA[i] > versionB[i])
68 else if (versionA[i] < versionB[i])
72 // If we come here, the versions are either the same or versionB has an extra component, just return false
76 static inline String safariPluginsDirectory()
78 static String pluginsDirectory;
79 static bool cachedPluginDirectory = false;
81 if (!cachedPluginDirectory) {
82 cachedPluginDirectory = true;
84 WCHAR moduleFileNameStr[MAX_PATH];
85 int moduleFileNameLen = ::GetModuleFileNameW(0, moduleFileNameStr, WTF_ARRAY_LENGTH(moduleFileNameStr));
87 if (!moduleFileNameLen || moduleFileNameLen == WTF_ARRAY_LENGTH(moduleFileNameStr))
88 return pluginsDirectory;
90 if (!::PathRemoveFileSpecW(moduleFileNameStr))
91 return pluginsDirectory;
93 pluginsDirectory = String(moduleFileNameStr) + "\\Plugins";
96 return pluginsDirectory;
99 static inline void addMozillaPluginDirectories(Vector<String>& directories)
101 // Enumerate all Mozilla plugin directories in the registry
103 LONG result = ::RegOpenKeyExW(HKEY_LOCAL_MACHINE, L"Software\\Mozilla", 0, KEY_READ, &key);
104 if (result != ERROR_SUCCESS)
108 FILETIME lastModified;
111 for (int i = 0;; i++) {
112 DWORD nameLen = WTF_ARRAY_LENGTH(name);
113 result = ::RegEnumKeyExW(key, i, name, &nameLen, 0, 0, 0, &lastModified);
115 if (result != ERROR_SUCCESS)
118 String extensionsPath = String(name, nameLen) + "\\Extensions";
121 // Try opening the key
122 result = ::RegOpenKeyExW(key, extensionsPath.charactersWithNullTermination(), 0, KEY_READ, &extensionsKey);
124 if (result == ERROR_SUCCESS) {
125 // Now get the plugins directory
126 WCHAR pluginsDirectoryStr[MAX_PATH];
127 DWORD pluginsDirectorySize = sizeof(pluginsDirectoryStr);
130 result = ::RegQueryValueExW(extensionsKey, L"Plugins", 0, &type, reinterpret_cast<LPBYTE>(&pluginsDirectoryStr), &pluginsDirectorySize);
132 if (result == ERROR_SUCCESS && type == REG_SZ)
133 directories.append(String(pluginsDirectoryStr, pluginsDirectorySize / sizeof(WCHAR) - 1));
135 ::RegCloseKey(extensionsKey);
142 static inline void addWindowsMediaPlayerPluginDirectory(Vector<String>& directories)
144 // The new WMP Firefox plugin is installed in \PFiles\Plugins if it can't find any Firefox installs
145 WCHAR pluginDirectoryStr[MAX_PATH + 1];
146 DWORD pluginDirectorySize = ::ExpandEnvironmentStringsW(L"%SYSTEMDRIVE%\\PFiles\\Plugins", pluginDirectoryStr, WTF_ARRAY_LENGTH(pluginDirectoryStr));
148 if (pluginDirectorySize > 0 && pluginDirectorySize <= WTF_ARRAY_LENGTH(pluginDirectoryStr))
149 directories.append(String(pluginDirectoryStr, pluginDirectorySize - 1));
152 WCHAR installationDirectoryStr[MAX_PATH];
153 DWORD installationDirectorySize = sizeof(installationDirectoryStr);
155 HRESULT result = ::SHGetValueW(HKEY_LOCAL_MACHINE, L"Software\\Microsoft\\MediaPlayer", L"Installation Directory", &type, reinterpret_cast<LPBYTE>(&installationDirectoryStr), &installationDirectorySize);
157 if (result == ERROR_SUCCESS && type == REG_SZ)
158 directories.append(String(installationDirectoryStr, installationDirectorySize / sizeof(WCHAR) - 1));
161 static inline void addQuickTimePluginDirectory(Vector<String>& directories)
164 WCHAR installationDirectoryStr[MAX_PATH];
165 DWORD installationDirectorySize = sizeof(installationDirectoryStr);
167 HRESULT result = ::SHGetValueW(HKEY_LOCAL_MACHINE, L"Software\\Apple Computer, Inc.\\QuickTime", L"InstallDir", &type, reinterpret_cast<LPBYTE>(&installationDirectoryStr), &installationDirectorySize);
169 if (result == ERROR_SUCCESS && type == REG_SZ) {
170 String pluginDir = String(installationDirectoryStr, installationDirectorySize / sizeof(WCHAR) - 1) + "\\plugins";
171 directories.append(pluginDir);
175 static inline void addAdobeAcrobatPluginDirectory(Vector<String>& directories)
178 HRESULT result = ::RegOpenKeyExW(HKEY_LOCAL_MACHINE, L"Software\\Adobe\\Acrobat Reader", 0, KEY_READ, &key);
179 if (result != ERROR_SUCCESS)
183 FILETIME lastModified;
185 Vector<int> latestAcrobatVersion;
186 String latestAcrobatVersionString;
189 for (int i = 0;; i++) {
190 DWORD nameLen = WTF_ARRAY_LENGTH(name);
191 result = ::RegEnumKeyExW(key, i, name, &nameLen, 0, 0, 0, &lastModified);
193 if (result != ERROR_SUCCESS)
196 Vector<int> acrobatVersion = parseVersionString(String(name, nameLen));
197 if (compareVersions(acrobatVersion, latestAcrobatVersion)) {
198 latestAcrobatVersion = acrobatVersion;
199 latestAcrobatVersionString = String(name, nameLen);
203 if (!latestAcrobatVersionString.isNull()) {
205 WCHAR acrobatInstallPathStr[MAX_PATH];
206 DWORD acrobatInstallPathSize = sizeof(acrobatInstallPathStr);
208 String acrobatPluginKeyPath = "Software\\Adobe\\Acrobat Reader\\" + latestAcrobatVersionString + "\\InstallPath";
209 result = ::SHGetValueW(HKEY_LOCAL_MACHINE, acrobatPluginKeyPath.charactersWithNullTermination(), 0, &type, reinterpret_cast<LPBYTE>(acrobatInstallPathStr), &acrobatInstallPathSize);
211 if (result == ERROR_SUCCESS) {
212 String acrobatPluginDirectory = String(acrobatInstallPathStr, acrobatInstallPathSize / sizeof(WCHAR) - 1) + "\\browser";
213 directories.append(acrobatPluginDirectory);
220 static inline void addMacromediaPluginDirectories(Vector<String>& directories)
223 WCHAR systemDirectoryStr[MAX_PATH];
225 if (!::GetSystemDirectoryW(systemDirectoryStr, WTF_ARRAY_LENGTH(systemDirectoryStr)))
228 WCHAR macromediaDirectoryStr[MAX_PATH];
230 if (!::PathCombineW(macromediaDirectoryStr, systemDirectoryStr, L"macromed\\Flash"))
233 directories.append(macromediaDirectoryStr);
235 if (!::PathCombineW(macromediaDirectoryStr, systemDirectoryStr, L"macromed\\Shockwave 10"))
238 directories.append(macromediaDirectoryStr);
242 Vector<String> PluginInfoStore::pluginsDirectories()
244 Vector<String> directories;
246 String ourDirectory = safariPluginsDirectory();
247 if (!ourDirectory.isNull())
248 directories.append(ourDirectory);
250 addQuickTimePluginDirectory(directories);
251 addAdobeAcrobatPluginDirectory(directories);
252 addMozillaPluginDirectories(directories);
253 addWindowsMediaPlayerPluginDirectory(directories);
254 addMacromediaPluginDirectories(directories);
259 Vector<String> PluginInfoStore::pluginPathsInDirectory(const String& directory)
261 Vector<String> paths;
263 PathWalker walker(directory, "*");
264 if (!walker.isValid())
268 if (walker.data().dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)
271 String filename = walker.data().cFileName;
272 if ((!filename.startsWith("np", false) || !filename.endsWith("dll", false)) && (!equalIgnoringCase(filename, "Plugin.dll") || !directory.endsWith("Shockwave 10", false)))
275 paths.append(directory + "\\" + filename);
276 } while (walker.step());
281 static void addPluginPathsFromRegistry(HKEY rootKey, Vector<String>& paths)
284 if (::RegOpenKeyExW(rootKey, L"Software\\MozillaPlugins", 0, KEY_ENUMERATE_SUB_KEYS, &key) != ERROR_SUCCESS)
287 for (size_t i = 0; ; ++i) {
288 // MSDN says that key names have a maximum length of 255 characters.
290 DWORD nameLen = WTF_ARRAY_LENGTH(name);
291 if (::RegEnumKeyExW(key, i, name, &nameLen, 0, 0, 0, 0) != ERROR_SUCCESS)
294 wchar_t path[MAX_PATH];
295 DWORD pathSizeInBytes = sizeof(path);
297 if (::SHGetValueW(key, name, L"Path", &type, path, &pathSizeInBytes) != ERROR_SUCCESS)
308 Vector<String> PluginInfoStore::individualPluginPaths()
310 Vector<String> paths;
312 addPluginPathsFromRegistry(HKEY_LOCAL_MACHINE, paths);
313 addPluginPathsFromRegistry(HKEY_CURRENT_USER, paths);
318 static uint64_t fileVersion(DWORD leastSignificant, DWORD mostSignificant)
320 ULARGE_INTEGER version;
321 version.LowPart = leastSignificant;
322 version.HighPart = mostSignificant;
323 return version.QuadPart;
326 bool PluginInfoStore::getPluginInfo(const String& pluginPath, PluginModuleInfo& plugin)
328 return NetscapePluginModule::getPluginInfo(pluginPath, plugin);
331 static bool isOldWindowsMediaPlayerPlugin(const PluginModuleInfo& plugin)
333 return equalIgnoringCase(plugin.info.file, "npdsplay.dll");
336 static bool isNewWindowsMediaPlayerPlugin(const PluginModuleInfo& plugin)
338 return equalIgnoringCase(plugin.info.file, "np-mswmp.dll");
341 bool PluginInfoStore::shouldUsePlugin(Vector<PluginModuleInfo>& alreadyLoadedPlugins, const PluginModuleInfo& plugin)
343 if (plugin.info.name == "Citrix ICA Client") {
344 // The Citrix ICA Client plug-in requires a Mozilla-based browser; see <rdar://6418681>.
348 if (plugin.info.name == "Silverlight Plug-In") {
349 // workaround for <rdar://5557379> Crash in Silverlight when opening microsoft.com.
350 // the latest 1.0 version of Silverlight does not reproduce this crash, so allow it
351 // and any newer versions
352 static const uint64_t minimumRequiredVersion = fileVersion(0x51BE0000, 0x00010000);
353 return plugin.fileVersion >= minimumRequiredVersion;
356 if (equalIgnoringCase(plugin.info.file, "npmozax.dll")) {
357 // Bug 15217: Mozilla ActiveX control complains about missing xpcom_core.dll
361 if (equalIgnoringCase(plugin.info.file, "npwpf.dll")) {
362 // Bug 57119: Microsoft Windows Presentation Foundation (WPF) plug-in complains about missing xpcom.dll
366 if (plugin.info.name == "Yahoo Application State Plugin") {
367 // https://bugs.webkit.org/show_bug.cgi?id=26860
368 // Bug in Yahoo Application State plug-in earlier than 1.0.0.6 leads to heap corruption.
369 static const uint64_t minimumRequiredVersion = fileVersion(0x00000006, 0x00010000);
370 return plugin.fileVersion >= minimumRequiredVersion;
373 if (isOldWindowsMediaPlayerPlugin(plugin)) {
374 // Don't load the old Windows Media Player plugin if we've already loaded the new Windows
375 // Media Player plugin.
376 for (size_t i = 0; i < alreadyLoadedPlugins.size(); ++i) {
377 if (!isNewWindowsMediaPlayerPlugin(alreadyLoadedPlugins[i]))
384 if (isNewWindowsMediaPlayerPlugin(plugin)) {
385 // Remove the old Windows Media Player plugin if we've already added it.
386 for (size_t i = 0; i < alreadyLoadedPlugins.size(); ++i) {
387 if (!isOldWindowsMediaPlayerPlugin(alreadyLoadedPlugins[i]))
389 alreadyLoadedPlugins.remove(i);
394 // FIXME: We should prefer a newer version of a plugin to an older version, rather than loading
395 // only the first. <http://webkit.org/b/58469>
396 String pluginFileName = pathGetFileName(plugin.path);
397 for (size_t i = 0; i < alreadyLoadedPlugins.size(); ++i) {
398 const PluginModuleInfo& loadedPlugin = alreadyLoadedPlugins[i];
400 // If a plug-in with the same filename already exists, we don't want to load it.
401 if (equalIgnoringCase(pluginFileName, pathGetFileName(loadedPlugin.path)))
408 } // namespace WebKit