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