1/* 2 * Copyright (C) 2006, 2008 Apple Inc. All rights reserved. 3 * Copyright (C) 2008 Collabora, Ltd. All rights reserved. 4 * Copyright (C) 2009 Torch Mobile, Inc. All rights reserved. 5 * 6 * Redistribution and use in source and binary forms, with or without 7 * modification, are permitted provided that the following conditions 8 * are met: 9 * 1. Redistributions of source code must retain the above copyright 10 * notice, this list of conditions and the following disclaimer. 11 * 2. Redistributions in binary form must reproduce the above copyright 12 * notice, this list of conditions and the following disclaimer in the 13 * documentation and/or other materials provided with the distribution. 14 * 15 * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY 16 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 17 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 18 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR 19 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, 20 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, 21 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR 22 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY 23 * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 24 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 25 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 26 */ 27 28 29#include "config.h" 30#include "PluginPackage.h" 31 32#include "MIMETypeRegistry.h" 33#include "PluginDatabase.h" 34#include "PluginDebug.h" 35#include "Timer.h" 36#include "npruntime_impl.h" 37#include <string.h> 38#include <wtf/OwnArrayPtr.h> 39#include <wtf/text/CString.h> 40#include <shlwapi.h> 41 42namespace WebCore { 43 44static String getVersionInfo(const LPVOID versionInfoData, const String& info) 45{ 46 LPVOID buffer; 47 UINT bufferLength; 48 String subInfo = "\\StringfileInfo\\040904E4\\" + info; 49 bool retval = VerQueryValueW(versionInfoData, 50 const_cast<UChar*>(subInfo.charactersWithNullTermination()), 51 &buffer, &bufferLength); 52 if (!retval || bufferLength == 0) 53 return String(); 54 55 // Subtract 1 from the length; we don't want the trailing null character. 56 return String(reinterpret_cast<UChar*>(buffer), bufferLength - 1); 57} 58 59bool PluginPackage::isPluginBlacklisted() 60{ 61 if (name() == "Citrix ICA Client") { 62 // The Citrix ICA Client plug-in requires a Mozilla-based browser; see <rdar://6418681>. 63 return true; 64 } 65 66 if (name() == "Silverlight Plug-In") { 67 // workaround for <rdar://5557379> Crash in Silverlight when opening microsoft.com. 68 // the latest 1.0 version of Silverlight does not reproduce this crash, so allow it 69 // and any newer versions 70 static const PlatformModuleVersion slPluginMinRequired(0x51BE0000, 0x00010000); 71 72 if (compareFileVersion(slPluginMinRequired) < 0) 73 return true; 74 } else if (fileName() == "npmozax.dll") { 75 // Bug 15217: Mozilla ActiveX control complains about missing xpcom_core.dll 76 return true; 77 } else if (fileName() == "npwpf.dll") { 78 // Bug 57119: Microsoft Windows Presentation Foundation (WPF) plug-in complains about missing xpcom.dll 79 return true; 80 } else if (name() == "Yahoo Application State Plugin") { 81 // https://bugs.webkit.org/show_bug.cgi?id=26860 82 // Bug in Yahoo Application State plug-in earlier than 1.0.0.6 leads to heap corruption. 83 static const PlatformModuleVersion yahooAppStatePluginMinRequired(0x00000006, 0x00010000); 84 if (compareFileVersion(yahooAppStatePluginMinRequired) < 0) 85 return true; 86 } 87 88 return false; 89} 90 91void PluginPackage::determineQuirks(const String& mimeType) 92{ 93 if (mimeType == "application/x-shockwave-flash") { 94 static const PlatformModuleVersion flashTenVersion(0x00000000, 0x000a0000); 95 96 // Pre 10 Flash only requests windowless plugins if we return a mozilla user agent 97 if (compareFileVersion(flashTenVersion) < 0) 98 m_quirks.add(PluginQuirkWantsMozillaUserAgent); 99 100 m_quirks.add(PluginQuirkThrottleInvalidate); 101 m_quirks.add(PluginQuirkThrottleWMUserPlusOneMessages); 102 m_quirks.add(PluginQuirkFlashURLNotifyBug); 103 } 104 105 if (name().contains("Microsoft") && name().contains("Windows Media")) { 106 // The WMP plugin sets its size on the first NPP_SetWindow call and never updates its size, so 107 // call SetWindow when the plugin view has a correct size 108 m_quirks.add(PluginQuirkDeferFirstSetWindowCall); 109 110 // Windowless mode does not work at all with the WMP plugin so just remove that parameter 111 // and don't pass it to the plug-in. 112 m_quirks.add(PluginQuirkRemoveWindowlessVideoParam); 113 114 // WMP has a modal message loop that it enters whenever we call it or 115 // ask it to paint. This modal loop can deliver messages to other 116 // windows in WebKit at times when they are not expecting them (for 117 // example, delivering a WM_PAINT message during a layout), and these 118 // can cause crashes. 119 m_quirks.add(PluginQuirkHasModalMessageLoop); 120 } 121 122 if (name() == "VLC Multimedia Plugin" || name() == "VLC Multimedia Plug-in") { 123 // VLC hangs on NPP_Destroy if we call NPP_SetWindow with a null window handle 124 m_quirks.add(PluginQuirkDontSetNullWindowHandleOnDestroy); 125 126 // VLC 0.8.6d and 0.8.6e crash if multiple instances are created. 127 // <rdar://problem/5773070> tracks allowing multiple instances when this 128 // bug is fixed. 129 m_quirks.add(PluginQuirkDontAllowMultipleInstances); 130 } 131 132 // The DivX plugin sets its size on the first NPP_SetWindow call and never updates its size, so 133 // call SetWindow when the plugin view has a correct size 134 if (mimeType == "video/divx") 135 m_quirks.add(PluginQuirkDeferFirstSetWindowCall); 136 137 // FIXME: This is a workaround for a problem in our NPRuntime bindings; if a plug-in creates an 138 // NPObject and passes it to a function it's not possible to see what root object that NPObject belongs to. 139 // Thus, we don't know that the object should be invalidated when the plug-in instance goes away. 140 // See <rdar://problem/5487742>. 141 if (mimeType == "application/x-silverlight") 142 m_quirks.add(PluginQuirkDontUnloadPlugin); 143 144 if (MIMETypeRegistry::isJavaAppletMIMEType(mimeType)) { 145 // Because a single process cannot create multiple VMs, and we cannot reliably unload a 146 // Java VM, we cannot unload the Java plugin, or we'll lose reference to our only VM 147 m_quirks.add(PluginQuirkDontUnloadPlugin); 148 149 // Setting the window region to an empty region causes bad scrolling repaint problems 150 // with the Java plug-in. 151 m_quirks.add(PluginQuirkDontClipToZeroRectWhenScrolling); 152 } 153 154 if (mimeType == "audio/x-pn-realaudio-plugin") { 155 // Prevent the Real plugin from calling the Window Proc recursively, causing the stack to overflow. 156 m_quirks.add(PluginQuirkDontCallWndProcForSameMessageRecursively); 157 158 static const PlatformModuleVersion lastKnownUnloadableRealPlayerVersion(0x000B0B24, 0x00060000); 159 160 // Unloading RealPlayer versions newer than 10.5 can cause a hang; see rdar://5669317. 161 // FIXME: Resume unloading when this bug in the RealPlayer Plug-In is fixed (rdar://5713147) 162 if (compareFileVersion(lastKnownUnloadableRealPlayerVersion) > 0) 163 m_quirks.add(PluginQuirkDontUnloadPlugin); 164 } 165} 166 167bool PluginPackage::fetchInfo() 168{ 169 DWORD versionInfoSize, zeroHandle; 170 versionInfoSize = GetFileVersionInfoSizeW(const_cast<UChar*>(m_path.charactersWithNullTermination()), &zeroHandle); 171 if (versionInfoSize == 0) 172 return false; 173 174 OwnArrayPtr<char> versionInfoData = adoptArrayPtr(new char[versionInfoSize]); 175 176 if (!GetFileVersionInfoW(const_cast<UChar*>(m_path.charactersWithNullTermination()), 177 0, versionInfoSize, versionInfoData.get())) 178 return false; 179 180 m_name = getVersionInfo(versionInfoData.get(), "ProductName"); 181 m_description = getVersionInfo(versionInfoData.get(), "FileDescription"); 182 if (m_name.isNull() || m_description.isNull()) 183 return false; 184 185 VS_FIXEDFILEINFO* info; 186 UINT infoSize; 187 if (!VerQueryValueW(versionInfoData.get(), L"\\", (LPVOID*) &info, &infoSize) || infoSize < sizeof(VS_FIXEDFILEINFO)) 188 return false; 189 m_moduleVersion.leastSig = info->dwFileVersionLS; 190 m_moduleVersion.mostSig = info->dwFileVersionMS; 191 192 if (isPluginBlacklisted()) 193 return false; 194 195 Vector<String> types; 196 getVersionInfo(versionInfoData.get(), "MIMEType").split('|', types); 197 Vector<String> extensionLists; 198 getVersionInfo(versionInfoData.get(), "FileExtents").split('|', extensionLists); 199 Vector<String> descriptions; 200 getVersionInfo(versionInfoData.get(), "FileOpenName").split('|', descriptions); 201 202 for (unsigned i = 0; i < types.size(); i++) { 203 String type = types[i].lower(); 204 String description = i < descriptions.size() ? descriptions[i] : ""; 205 String extensionList = i < extensionLists.size() ? extensionLists[i] : ""; 206 207 Vector<String> extensionsVector; 208 extensionList.split(',', extensionsVector); 209 210 // Get rid of the extension list that may be at the end of the description string. 211 int pos = description.find("(*"); 212 if (pos != -1) { 213 // There might be a space that we need to get rid of. 214 if (pos > 1 && description[pos - 1] == ' ') 215 pos--; 216 description = description.left(pos); 217 } 218 219 // Determine the quirks for the MIME types this plug-in supports 220 determineQuirks(type); 221 222 m_mimeToExtensions.add(type, extensionsVector); 223 m_mimeToDescriptions.add(type, description); 224 } 225 226 return true; 227} 228 229bool PluginPackage::load() 230{ 231 if (m_freeLibraryTimer.isActive()) { 232 ASSERT(m_module); 233 m_freeLibraryTimer.stop(); 234 } else if (m_isLoaded) { 235 if (m_quirks.contains(PluginQuirkDontAllowMultipleInstances)) 236 return false; 237 m_loadCount++; 238 return true; 239 } else { 240#if OS(WINCE) 241 m_module = ::LoadLibraryW(m_path.charactersWithNullTermination()); 242#else 243 WCHAR currentPath[MAX_PATH]; 244 245 if (!::GetCurrentDirectoryW(MAX_PATH, currentPath)) 246 return false; 247 248 String path = m_path.substring(0, m_path.reverseFind('\\')); 249 250 if (!::SetCurrentDirectoryW(path.charactersWithNullTermination())) 251 return false; 252 253 // Load the library 254 m_module = ::LoadLibraryExW(m_path.charactersWithNullTermination(), 0, LOAD_WITH_ALTERED_SEARCH_PATH); 255 256 if (!::SetCurrentDirectoryW(currentPath)) { 257 if (m_module) 258 ::FreeLibrary(m_module); 259 return false; 260 } 261#endif 262 } 263 264 if (!m_module) 265 return false; 266 267 m_isLoaded = true; 268 269 NP_GetEntryPointsFuncPtr NP_GetEntryPoints = 0; 270 NP_InitializeFuncPtr NP_Initialize = 0; 271 NPError npErr; 272 273#if OS(WINCE) 274 NP_Initialize = (NP_InitializeFuncPtr)GetProcAddress(m_module, L"NP_Initialize"); 275 NP_GetEntryPoints = (NP_GetEntryPointsFuncPtr)GetProcAddress(m_module, L"NP_GetEntryPoints"); 276 m_NPP_Shutdown = (NPP_ShutdownProcPtr)GetProcAddress(m_module, L"NP_Shutdown"); 277#else 278 NP_Initialize = (NP_InitializeFuncPtr)GetProcAddress(m_module, "NP_Initialize"); 279 NP_GetEntryPoints = (NP_GetEntryPointsFuncPtr)GetProcAddress(m_module, "NP_GetEntryPoints"); 280 m_NPP_Shutdown = (NPP_ShutdownProcPtr)GetProcAddress(m_module, "NP_Shutdown"); 281#endif 282 283 if (!NP_Initialize || !NP_GetEntryPoints || !m_NPP_Shutdown) 284 goto abort; 285 286 memset(&m_pluginFuncs, 0, sizeof(m_pluginFuncs)); 287 m_pluginFuncs.size = sizeof(m_pluginFuncs); 288 289 npErr = NP_GetEntryPoints(&m_pluginFuncs); 290 LOG_NPERROR(npErr); 291 if (npErr != NPERR_NO_ERROR) 292 goto abort; 293 294 initializeBrowserFuncs(); 295 296 npErr = NP_Initialize(&m_browserFuncs); 297 LOG_NPERROR(npErr); 298 299 if (npErr != NPERR_NO_ERROR) 300 goto abort; 301 302 m_loadCount++; 303 return true; 304 305abort: 306 unloadWithoutShutdown(); 307 return false; 308} 309 310unsigned PluginPackage::hash() const 311{ 312 const unsigned hashCodes[] = { 313 m_name.impl()->hash(), 314 m_description.impl()->hash(), 315 m_mimeToExtensions.size() 316 }; 317 318 return StringHasher::hashMemory<sizeof(hashCodes)>(hashCodes); 319} 320 321bool PluginPackage::equal(const PluginPackage& a, const PluginPackage& b) 322{ 323 if (a.m_name != b.m_name) 324 return false; 325 326 if (a.m_description != b.m_description) 327 return false; 328 329 if (a.m_mimeToExtensions.size() != b.m_mimeToExtensions.size()) 330 return false; 331 332 MIMEToExtensionsMap::const_iterator::Keys end = a.m_mimeToExtensions.end().keys(); 333 for (MIMEToExtensionsMap::const_iterator::Keys it = a.m_mimeToExtensions.begin().keys(); it != end; ++it) { 334 if (!b.m_mimeToExtensions.contains(*it)) 335 return false; 336 } 337 338 return true; 339} 340 341uint16_t PluginPackage::NPVersion() const 342{ 343 return NP_VERSION_MINOR; 344} 345} 346