| /* |
| * Copyright (C) 2010 Apple Inc. All rights reserved. |
| * |
| * Redistribution and use in source and binary forms, with or without |
| * modification, are permitted provided that the following conditions |
| * are met: |
| * 1. Redistributions of source code must retain the above copyright |
| * notice, this list of conditions and the following disclaimer. |
| * 2. Redistributions in binary form must reproduce the above copyright |
| * notice, this list of conditions and the following disclaimer in the |
| * documentation and/or other materials provided with the distribution. |
| * |
| * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS'' |
| * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, |
| * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR |
| * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS |
| * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR |
| * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF |
| * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS |
| * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN |
| * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) |
| * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF |
| * THE POSSIBILITY OF SUCH DAMAGE. |
| */ |
| |
| #include "PluginInfoStore.h" |
| |
| #include "NetscapePluginModule.h" |
| #include <shlwapi.h> |
| |
| using namespace WebCore; |
| |
| namespace WebKit { |
| |
| static inline Vector<int> parseVersionString(const String& versionString) |
| { |
| Vector<int> version; |
| |
| unsigned startPos = 0; |
| unsigned endPos; |
| |
| while (startPos < versionString.length()) { |
| for (endPos = startPos; endPos < versionString.length(); ++endPos) |
| if (versionString[endPos] == '.' || versionString[endPos] == '_') |
| break; |
| |
| int versionComponent = versionString.substring(startPos, endPos - startPos).toInt(); |
| version.append(versionComponent); |
| |
| startPos = endPos + 1; |
| } |
| |
| return version; |
| } |
| |
| // This returns whether versionA is higher than versionB |
| static inline bool compareVersions(const Vector<int>& versionA, const Vector<int>& versionB) |
| { |
| for (unsigned i = 0; i < versionA.size(); i++) { |
| if (i >= versionB.size()) |
| return true; |
| |
| if (versionA[i] > versionB[i]) |
| return true; |
| else if (versionA[i] < versionB[i]) |
| return false; |
| } |
| |
| // If we come here, the versions are either the same or versionB has an extra component, just return false |
| return false; |
| } |
| |
| static inline String safariPluginsDirectory() |
| { |
| static String pluginsDirectory; |
| static bool cachedPluginDirectory = false; |
| |
| if (!cachedPluginDirectory) { |
| cachedPluginDirectory = true; |
| |
| WCHAR moduleFileNameStr[MAX_PATH]; |
| int moduleFileNameLen = ::GetModuleFileNameW(0, moduleFileNameStr, WTF_ARRAY_LENGTH(moduleFileNameStr)); |
| |
| if (!moduleFileNameLen || moduleFileNameLen == WTF_ARRAY_LENGTH(moduleFileNameStr)) |
| return pluginsDirectory; |
| |
| if (!::PathRemoveFileSpecW(moduleFileNameStr)) |
| return pluginsDirectory; |
| |
| pluginsDirectory = String(moduleFileNameStr) + "\\Plugins"; |
| } |
| |
| return pluginsDirectory; |
| } |
| |
| static inline void addMozillaPluginDirectories(Vector<String>& directories) |
| { |
| // Enumerate all Mozilla plugin directories in the registry |
| HKEY key; |
| LONG result = ::RegOpenKeyExW(HKEY_LOCAL_MACHINE, L"Software\\Mozilla", 0, KEY_READ, &key); |
| if (result != ERROR_SUCCESS) |
| return; |
| |
| WCHAR name[128]; |
| FILETIME lastModified; |
| |
| // Enumerate subkeys |
| for (int i = 0;; i++) { |
| DWORD nameLen = WTF_ARRAY_LENGTH(name); |
| result = ::RegEnumKeyExW(key, i, name, &nameLen, 0, 0, 0, &lastModified); |
| |
| if (result != ERROR_SUCCESS) |
| break; |
| |
| String extensionsPath = String(name, nameLen) + "\\Extensions"; |
| HKEY extensionsKey; |
| |
| // Try opening the key |
| result = ::RegOpenKeyExW(key, extensionsPath.charactersWithNullTermination(), 0, KEY_READ, &extensionsKey); |
| |
| if (result == ERROR_SUCCESS) { |
| // Now get the plugins directory |
| WCHAR pluginsDirectoryStr[MAX_PATH]; |
| DWORD pluginsDirectorySize = sizeof(pluginsDirectoryStr); |
| DWORD type; |
| |
| result = ::RegQueryValueExW(extensionsKey, L"Plugins", 0, &type, reinterpret_cast<LPBYTE>(&pluginsDirectoryStr), &pluginsDirectorySize); |
| |
| if (result == ERROR_SUCCESS && type == REG_SZ) |
| directories.append(String(pluginsDirectoryStr, pluginsDirectorySize / sizeof(WCHAR) - 1)); |
| |
| ::RegCloseKey(extensionsKey); |
| } |
| } |
| |
| ::RegCloseKey(key); |
| } |
| |
| static inline void addWindowsMediaPlayerPluginDirectory(Vector<String>& directories) |
| { |
| // The new WMP Firefox plugin is installed in \PFiles\Plugins if it can't find any Firefox installs |
| WCHAR pluginDirectoryStr[MAX_PATH + 1]; |
| DWORD pluginDirectorySize = ::ExpandEnvironmentStringsW(L"%SYSTEMDRIVE%\\PFiles\\Plugins", pluginDirectoryStr, WTF_ARRAY_LENGTH(pluginDirectoryStr)); |
| |
| if (pluginDirectorySize > 0 && pluginDirectorySize <= WTF_ARRAY_LENGTH(pluginDirectoryStr)) |
| directories.append(String(pluginDirectoryStr, pluginDirectorySize - 1)); |
| |
| DWORD type; |
| WCHAR installationDirectoryStr[MAX_PATH]; |
| DWORD installationDirectorySize = sizeof(installationDirectoryStr); |
| |
| HRESULT result = ::SHGetValueW(HKEY_LOCAL_MACHINE, L"Software\\Microsoft\\MediaPlayer", L"Installation Directory", &type, reinterpret_cast<LPBYTE>(&installationDirectoryStr), &installationDirectorySize); |
| |
| if (result == ERROR_SUCCESS && type == REG_SZ) |
| directories.append(String(installationDirectoryStr, installationDirectorySize / sizeof(WCHAR) - 1)); |
| } |
| |
| static inline void addQuickTimePluginDirectory(Vector<String>& directories) |
| { |
| DWORD type; |
| WCHAR installationDirectoryStr[MAX_PATH]; |
| DWORD installationDirectorySize = sizeof(installationDirectoryStr); |
| |
| HRESULT result = ::SHGetValueW(HKEY_LOCAL_MACHINE, L"Software\\Apple Computer, Inc.\\QuickTime", L"InstallDir", &type, reinterpret_cast<LPBYTE>(&installationDirectoryStr), &installationDirectorySize); |
| |
| if (result == ERROR_SUCCESS && type == REG_SZ) { |
| String pluginDir = String(installationDirectoryStr, installationDirectorySize / sizeof(WCHAR) - 1) + "\\plugins"; |
| directories.append(pluginDir); |
| } |
| } |
| |
| static inline void addAdobeAcrobatPluginDirectory(Vector<String>& directories) |
| { |
| HKEY key; |
| HRESULT result = ::RegOpenKeyExW(HKEY_LOCAL_MACHINE, L"Software\\Adobe\\Acrobat Reader", 0, KEY_READ, &key); |
| if (result != ERROR_SUCCESS) |
| return; |
| |
| WCHAR name[128]; |
| FILETIME lastModified; |
| |
| Vector<int> latestAcrobatVersion; |
| String latestAcrobatVersionString; |
| |
| // Enumerate subkeys |
| for (int i = 0;; i++) { |
| DWORD nameLen = WTF_ARRAY_LENGTH(name); |
| result = ::RegEnumKeyExW(key, i, name, &nameLen, 0, 0, 0, &lastModified); |
| |
| if (result != ERROR_SUCCESS) |
| break; |
| |
| Vector<int> acrobatVersion = parseVersionString(String(name, nameLen)); |
| if (compareVersions(acrobatVersion, latestAcrobatVersion)) { |
| latestAcrobatVersion = acrobatVersion; |
| latestAcrobatVersionString = String(name, nameLen); |
| } |
| } |
| |
| if (!latestAcrobatVersionString.isNull()) { |
| DWORD type; |
| WCHAR acrobatInstallPathStr[MAX_PATH]; |
| DWORD acrobatInstallPathSize = sizeof(acrobatInstallPathStr); |
| |
| String acrobatPluginKeyPath = "Software\\Adobe\\Acrobat Reader\\" + latestAcrobatVersionString + "\\InstallPath"; |
| result = ::SHGetValueW(HKEY_LOCAL_MACHINE, acrobatPluginKeyPath.charactersWithNullTermination(), 0, &type, reinterpret_cast<LPBYTE>(acrobatInstallPathStr), &acrobatInstallPathSize); |
| |
| if (result == ERROR_SUCCESS) { |
| String acrobatPluginDirectory = String(acrobatInstallPathStr, acrobatInstallPathSize / sizeof(WCHAR) - 1) + "\\browser"; |
| directories.append(acrobatPluginDirectory); |
| } |
| } |
| |
| ::RegCloseKey(key); |
| } |
| |
| static inline void addMacromediaPluginDirectories(Vector<String>& directories) |
| { |
| #if !OS(WINCE) |
| WCHAR systemDirectoryStr[MAX_PATH]; |
| |
| if (!::GetSystemDirectoryW(systemDirectoryStr, WTF_ARRAY_LENGTH(systemDirectoryStr))) |
| return; |
| |
| WCHAR macromediaDirectoryStr[MAX_PATH]; |
| |
| if (!::PathCombineW(macromediaDirectoryStr, systemDirectoryStr, L"macromed\\Flash")) |
| return; |
| |
| directories.append(macromediaDirectoryStr); |
| |
| if (!::PathCombineW(macromediaDirectoryStr, systemDirectoryStr, L"macromed\\Shockwave 10")) |
| return; |
| |
| directories.append(macromediaDirectoryStr); |
| #endif |
| } |
| |
| Vector<String> PluginInfoStore::pluginsDirectories() |
| { |
| Vector<String> directories; |
| |
| String ourDirectory = safariPluginsDirectory(); |
| if (!ourDirectory.isNull()) |
| directories.append(ourDirectory); |
| |
| addQuickTimePluginDirectory(directories); |
| addAdobeAcrobatPluginDirectory(directories); |
| addMozillaPluginDirectories(directories); |
| addWindowsMediaPlayerPluginDirectory(directories); |
| addMacromediaPluginDirectories(directories); |
| |
| return directories; |
| } |
| |
| class PathWalker { |
| WTF_MAKE_NONCOPYABLE(PathWalker); |
| public: |
| PathWalker(const String& directory) |
| { |
| String pattern = directory + "\\*"; |
| m_handle = ::FindFirstFileW(pattern.charactersWithNullTermination(), &m_data); |
| } |
| |
| ~PathWalker() |
| { |
| if (!isValid()) |
| return; |
| ::FindClose(m_handle); |
| } |
| |
| bool isValid() const { return m_handle != INVALID_HANDLE_VALUE; } |
| const WIN32_FIND_DATAW& data() const { return m_data; } |
| |
| bool step() { return ::FindNextFileW(m_handle, &m_data); } |
| |
| private: |
| HANDLE m_handle; |
| WIN32_FIND_DATAW m_data; |
| }; |
| |
| Vector<String> PluginInfoStore::pluginPathsInDirectory(const String& directory) |
| { |
| Vector<String> paths; |
| |
| PathWalker walker(directory); |
| if (!walker.isValid()) |
| return paths; |
| |
| do { |
| if (walker.data().dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) |
| continue; |
| |
| String filename = walker.data().cFileName; |
| if ((!filename.startsWith("np", false) || !filename.endsWith("dll", false)) && (!equalIgnoringCase(filename, "Plugin.dll") || !directory.endsWith("Shockwave 10", false))) |
| continue; |
| |
| paths.append(directory + "\\" + filename); |
| } while (walker.step()); |
| |
| return paths; |
| } |
| |
| static void addPluginPathsFromRegistry(HKEY rootKey, Vector<String>& paths) |
| { |
| HKEY key; |
| if (::RegOpenKeyExW(rootKey, L"Software\\MozillaPlugins", 0, KEY_ENUMERATE_SUB_KEYS, &key) != ERROR_SUCCESS) |
| return; |
| |
| for (size_t i = 0; ; ++i) { |
| // MSDN says that key names have a maximum length of 255 characters. |
| wchar_t name[256]; |
| DWORD nameLen = WTF_ARRAY_LENGTH(name); |
| if (::RegEnumKeyExW(key, i, name, &nameLen, 0, 0, 0, 0) != ERROR_SUCCESS) |
| break; |
| |
| wchar_t path[MAX_PATH]; |
| DWORD pathSizeInBytes = sizeof(path); |
| DWORD type; |
| if (::SHGetValueW(key, name, L"Path", &type, path, &pathSizeInBytes) != ERROR_SUCCESS) |
| continue; |
| if (type != REG_SZ) |
| continue; |
| |
| paths.append(path); |
| } |
| |
| ::RegCloseKey(key); |
| } |
| |
| Vector<String> PluginInfoStore::individualPluginPaths() |
| { |
| Vector<String> paths; |
| |
| addPluginPathsFromRegistry(HKEY_LOCAL_MACHINE, paths); |
| addPluginPathsFromRegistry(HKEY_CURRENT_USER, paths); |
| |
| return paths; |
| } |
| |
| static uint64_t fileVersion(DWORD leastSignificant, DWORD mostSignificant) |
| { |
| ULARGE_INTEGER version; |
| version.LowPart = leastSignificant; |
| version.HighPart = mostSignificant; |
| return version.QuadPart; |
| } |
| |
| bool PluginInfoStore::getPluginInfo(const String& pluginPath, Plugin& plugin) |
| { |
| return NetscapePluginModule::getPluginInfo(pluginPath, plugin); |
| } |
| |
| static bool isOldWindowsMediaPlayerPlugin(const PluginInfoStore::Plugin& plugin) |
| { |
| return equalIgnoringCase(plugin.info.file, "npdsplay.dll"); |
| } |
| |
| static bool isNewWindowsMediaPlayerPlugin(const PluginInfoStore::Plugin& plugin) |
| { |
| return equalIgnoringCase(plugin.info.file, "np-mswmp.dll"); |
| } |
| |
| bool PluginInfoStore::shouldUsePlugin(const Plugin& plugin) |
| { |
| // FIXME: We should prefer a newer version of a plugin to an older version, rather than loading |
| // both. <http://webkit.org/b/49075> |
| |
| if (plugin.info.name == "Citrix ICA Client") { |
| // The Citrix ICA Client plug-in requires a Mozilla-based browser; see <rdar://6418681>. |
| return false; |
| } |
| |
| if (plugin.info.name == "Silverlight Plug-In") { |
| // workaround for <rdar://5557379> Crash in Silverlight when opening microsoft.com. |
| // the latest 1.0 version of Silverlight does not reproduce this crash, so allow it |
| // and any newer versions |
| static const uint64_t minimumRequiredVersion = fileVersion(0x51BE0000, 0x00010000); |
| return plugin.fileVersion >= minimumRequiredVersion; |
| } |
| |
| if (equalIgnoringCase(plugin.info.file, "npmozax.dll")) { |
| // Bug 15217: Mozilla ActiveX control complains about missing xpcom_core.dll |
| return false; |
| } |
| |
| if (plugin.info.name == "Yahoo Application State Plugin") { |
| // https://bugs.webkit.org/show_bug.cgi?id=26860 |
| // Bug in Yahoo Application State plug-in earlier than 1.0.0.6 leads to heap corruption. |
| static const uint64_t minimumRequiredVersion = fileVersion(0x00000006, 0x00010000); |
| return plugin.fileVersion >= minimumRequiredVersion; |
| } |
| |
| if (isOldWindowsMediaPlayerPlugin(plugin)) { |
| // Don't load the old Windows Media Player plugin if we've already loaded the new Windows |
| // Media Player plugin. |
| for (size_t i = 0; i < m_plugins.size(); ++i) { |
| if (!isNewWindowsMediaPlayerPlugin(m_plugins[i])) |
| continue; |
| return false; |
| } |
| return true; |
| } |
| |
| if (isNewWindowsMediaPlayerPlugin(plugin)) { |
| // Unload the old Windows Media Player plugin if we've already loaded it. |
| for (size_t i = 0; i < m_plugins.size(); ++i) { |
| if (!isOldWindowsMediaPlayerPlugin(m_plugins[i])) |
| continue; |
| m_plugins.remove(i); |
| } |
| return true; |
| } |
| |
| return true; |
| } |
| |
| } // namespace WebKit |