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