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