|
1 /* |
|
2 * Copyright (C) 2006, 2008 Apple Inc. All rights reserved. |
|
3 * Copyright (C) 2008 Collabora Ltd. All rights reserved. |
|
4 * Copyright (C) 2009 Holger Hans Peter Freyther |
|
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 #include "config.h" |
|
29 #include "PluginPackage.h" |
|
30 |
|
31 #include "MIMETypeRegistry.h" |
|
32 #include "PluginDatabase.h" |
|
33 #include "PluginDebug.h" |
|
34 #include "Timer.h" |
|
35 #include "npruntime_impl.h" |
|
36 #include <string.h> |
|
37 #include <wtf/OwnArrayPtr.h> |
|
38 #include <wtf/text/CString.h> |
|
39 |
|
40 namespace WebCore { |
|
41 |
|
42 PluginPackage::~PluginPackage() |
|
43 { |
|
44 // This destructor gets called during refresh() if PluginDatabase's |
|
45 // PluginSet hash is already populated, as it removes items from |
|
46 // the hash table. Calling the destructor on a loaded plug-in of |
|
47 // course would cause a crash, so we check to call unload before we |
|
48 // ASSERT. |
|
49 // FIXME: There is probably a better way to fix this. |
|
50 if (!m_loadCount) |
|
51 unloadWithoutShutdown(); |
|
52 else |
|
53 unload(); |
|
54 |
|
55 ASSERT(!m_isLoaded); |
|
56 } |
|
57 |
|
58 void PluginPackage::freeLibrarySoon() |
|
59 { |
|
60 ASSERT(!m_freeLibraryTimer.isActive()); |
|
61 ASSERT(m_module); |
|
62 ASSERT(!m_loadCount); |
|
63 |
|
64 m_freeLibraryTimer.startOneShot(0); |
|
65 } |
|
66 |
|
67 void PluginPackage::freeLibraryTimerFired(Timer<PluginPackage>*) |
|
68 { |
|
69 ASSERT(m_module); |
|
70 ASSERT(!m_loadCount); |
|
71 |
|
72 unloadModule(m_module); |
|
73 m_module = 0; |
|
74 } |
|
75 |
|
76 |
|
77 int PluginPackage::compare(const PluginPackage& compareTo) const |
|
78 { |
|
79 // Sort plug-ins that allow multiple instances first. |
|
80 bool AallowsMultipleInstances = !quirks().contains(PluginQuirkDontAllowMultipleInstances); |
|
81 bool BallowsMultipleInstances = !compareTo.quirks().contains(PluginQuirkDontAllowMultipleInstances); |
|
82 if (AallowsMultipleInstances != BallowsMultipleInstances) |
|
83 return AallowsMultipleInstances ? -1 : 1; |
|
84 |
|
85 // Sort plug-ins in a preferred path first. |
|
86 bool AisInPreferredDirectory = PluginDatabase::isPreferredPluginDirectory(parentDirectory()); |
|
87 bool BisInPreferredDirectory = PluginDatabase::isPreferredPluginDirectory(compareTo.parentDirectory()); |
|
88 if (AisInPreferredDirectory != BisInPreferredDirectory) |
|
89 return AisInPreferredDirectory ? -1 : 1; |
|
90 |
|
91 int diff = strcmp(name().utf8().data(), compareTo.name().utf8().data()); |
|
92 if (diff) |
|
93 return diff; |
|
94 |
|
95 diff = compareFileVersion(compareTo.version()); |
|
96 if (diff) |
|
97 return diff; |
|
98 |
|
99 return strcmp(parentDirectory().utf8().data(), compareTo.parentDirectory().utf8().data()); |
|
100 } |
|
101 |
|
102 PluginPackage::PluginPackage(const String& path, const time_t& lastModified) |
|
103 : m_isEnabled(true) |
|
104 , m_isLoaded(false) |
|
105 , m_loadCount(0) |
|
106 , m_path(path) |
|
107 , m_moduleVersion(0) |
|
108 , m_module(0) |
|
109 , m_lastModified(lastModified) |
|
110 , m_freeLibraryTimer(this, &PluginPackage::freeLibraryTimerFired) |
|
111 { |
|
112 m_fileName = pathGetFileName(m_path); |
|
113 m_parentDirectory = m_path.left(m_path.length() - m_fileName.length() - 1); |
|
114 } |
|
115 |
|
116 #if !OS(SYMBIAN) |
|
117 void PluginPackage::unload() |
|
118 { |
|
119 if (!m_isLoaded) |
|
120 return; |
|
121 |
|
122 if (--m_loadCount > 0) |
|
123 return; |
|
124 |
|
125 m_NPP_Shutdown(); |
|
126 |
|
127 unloadWithoutShutdown(); |
|
128 } |
|
129 #endif // !OS(SYMBIAN) |
|
130 |
|
131 void PluginPackage::unloadWithoutShutdown() |
|
132 { |
|
133 if (!m_isLoaded) |
|
134 return; |
|
135 |
|
136 ASSERT(!m_loadCount); |
|
137 ASSERT(m_module); |
|
138 |
|
139 // <rdar://5530519>: Crash when closing tab with pdf file (Reader 7 only) |
|
140 // If the plugin has subclassed its parent window, as with Reader 7, we may have |
|
141 // gotten here by way of the plugin's internal window proc forwarding a message to our |
|
142 // original window proc. If we free the plugin library from here, we will jump back |
|
143 // to code we just freed when we return, so delay calling FreeLibrary at least until |
|
144 // the next message loop |
|
145 freeLibrarySoon(); |
|
146 |
|
147 m_isLoaded = false; |
|
148 } |
|
149 |
|
150 void PluginPackage::setEnabled(bool enabled) |
|
151 { |
|
152 m_isEnabled = enabled; |
|
153 } |
|
154 |
|
155 PassRefPtr<PluginPackage> PluginPackage::createPackage(const String& path, const time_t& lastModified) |
|
156 { |
|
157 RefPtr<PluginPackage> package = adoptRef(new PluginPackage(path, lastModified)); |
|
158 |
|
159 if (!package->fetchInfo()) |
|
160 return 0; |
|
161 |
|
162 return package.release(); |
|
163 } |
|
164 |
|
165 #if defined(XP_UNIX) |
|
166 void PluginPackage::determineQuirks(const String& mimeType) |
|
167 { |
|
168 if (MIMETypeRegistry::isJavaAppletMIMEType(mimeType)) { |
|
169 // Because a single process cannot create multiple VMs, and we cannot reliably unload a |
|
170 // Java VM, we cannot unload the Java plugin, or we'll lose reference to our only VM |
|
171 m_quirks.add(PluginQuirkDontUnloadPlugin); |
|
172 |
|
173 // Setting the window region to an empty region causes bad scrolling repaint problems |
|
174 // with the Java plug-in. |
|
175 m_quirks.add(PluginQuirkDontClipToZeroRectWhenScrolling); |
|
176 return; |
|
177 } |
|
178 |
|
179 if (mimeType == "application/x-shockwave-flash") { |
|
180 static const PlatformModuleVersion flashTenVersion(0x0a000000); |
|
181 |
|
182 if (compareFileVersion(flashTenVersion) >= 0) { |
|
183 // Flash 10.0 b218 doesn't like having a NULL window handle |
|
184 m_quirks.add(PluginQuirkDontSetNullWindowHandleOnDestroy); |
|
185 #if PLATFORM(QT) |
|
186 m_quirks.add(PluginQuirkRequiresGtkToolKit); |
|
187 #endif |
|
188 m_quirks.add(PluginQuirkRequiresDefaultScreenDepth); |
|
189 } else { |
|
190 // Flash 9 and older requests windowless plugins if we return a mozilla user agent |
|
191 m_quirks.add(PluginQuirkWantsMozillaUserAgent); |
|
192 #if PLATFORM(QT) |
|
193 // Flash 9 and older would crash on repeated calls to SetWindow in windowed mode |
|
194 m_quirks.add(PluginQuirkDontCallSetWindowMoreThanOnce); |
|
195 #endif |
|
196 } |
|
197 |
|
198 m_quirks.add(PluginQuirkThrottleInvalidate); |
|
199 m_quirks.add(PluginQuirkThrottleWMUserPlusOneMessages); |
|
200 m_quirks.add(PluginQuirkFlashURLNotifyBug); |
|
201 } |
|
202 } |
|
203 #endif |
|
204 |
|
205 #if !OS(WINDOWS) |
|
206 void PluginPackage::determineModuleVersionFromDescription() |
|
207 { |
|
208 // It's a bit lame to detect the plugin version by parsing it |
|
209 // from the plugin description string, but it doesn't seem that |
|
210 // version information is available in any standardized way at |
|
211 // the module level, like in Windows |
|
212 |
|
213 if (m_description.isEmpty()) |
|
214 return; |
|
215 |
|
216 if (m_description.startsWith("Shockwave Flash") && m_description.length() >= 19) { |
|
217 // The flash version as a PlatformModuleVersion differs on Unix from Windows |
|
218 // since the revision can be larger than a 8 bits, so we allow it 16 here and |
|
219 // push the major/minor up 8 bits. Thus on Unix, Flash's version may be |
|
220 // 0x0a000000 instead of 0x000a0000. |
|
221 |
|
222 Vector<String> versionParts; |
|
223 m_description.substring(16).split(' ', /*allowEmptyEntries =*/ false, versionParts); |
|
224 if (versionParts.isEmpty()) |
|
225 return; |
|
226 |
|
227 if (versionParts.size() >= 1) { |
|
228 Vector<String> majorMinorParts; |
|
229 versionParts[0].split('.', majorMinorParts); |
|
230 if (majorMinorParts.size() >= 1) { |
|
231 bool converted = false; |
|
232 unsigned major = majorMinorParts[0].toUInt(&converted); |
|
233 if (converted) |
|
234 m_moduleVersion = (major & 0xff) << 24; |
|
235 } |
|
236 if (majorMinorParts.size() == 2) { |
|
237 bool converted = false; |
|
238 unsigned minor = majorMinorParts[1].toUInt(&converted); |
|
239 if (converted) |
|
240 m_moduleVersion |= (minor & 0xff) << 16; |
|
241 } |
|
242 } |
|
243 |
|
244 if (versionParts.size() >= 2) { |
|
245 String revision = versionParts[1]; |
|
246 if (revision.length() > 1 && (revision[0] == 'r' || revision[0] == 'b')) { |
|
247 revision.remove(0, 1); |
|
248 m_moduleVersion |= revision.toInt() & 0xffff; |
|
249 } |
|
250 } |
|
251 } |
|
252 } |
|
253 #endif |
|
254 |
|
255 #if ENABLE(NETSCAPE_PLUGIN_API) |
|
256 void PluginPackage::initializeBrowserFuncs() |
|
257 { |
|
258 memset(&m_browserFuncs, 0, sizeof(m_browserFuncs)); |
|
259 m_browserFuncs.size = sizeof(m_browserFuncs); |
|
260 m_browserFuncs.version = NPVersion(); |
|
261 |
|
262 m_browserFuncs.geturl = NPN_GetURL; |
|
263 m_browserFuncs.posturl = NPN_PostURL; |
|
264 m_browserFuncs.requestread = NPN_RequestRead; |
|
265 m_browserFuncs.newstream = NPN_NewStream; |
|
266 m_browserFuncs.write = NPN_Write; |
|
267 m_browserFuncs.destroystream = NPN_DestroyStream; |
|
268 m_browserFuncs.status = NPN_Status; |
|
269 m_browserFuncs.uagent = NPN_UserAgent; |
|
270 m_browserFuncs.memalloc = NPN_MemAlloc; |
|
271 m_browserFuncs.memfree = NPN_MemFree; |
|
272 m_browserFuncs.memflush = NPN_MemFlush; |
|
273 m_browserFuncs.reloadplugins = NPN_ReloadPlugins; |
|
274 m_browserFuncs.geturlnotify = NPN_GetURLNotify; |
|
275 m_browserFuncs.posturlnotify = NPN_PostURLNotify; |
|
276 m_browserFuncs.getvalue = NPN_GetValue; |
|
277 m_browserFuncs.setvalue = NPN_SetValue; |
|
278 m_browserFuncs.invalidaterect = NPN_InvalidateRect; |
|
279 m_browserFuncs.invalidateregion = NPN_InvalidateRegion; |
|
280 m_browserFuncs.forceredraw = NPN_ForceRedraw; |
|
281 m_browserFuncs.getJavaEnv = NPN_GetJavaEnv; |
|
282 m_browserFuncs.getJavaPeer = NPN_GetJavaPeer; |
|
283 m_browserFuncs.pushpopupsenabledstate = NPN_PushPopupsEnabledState; |
|
284 m_browserFuncs.poppopupsenabledstate = NPN_PopPopupsEnabledState; |
|
285 m_browserFuncs.pluginthreadasynccall = NPN_PluginThreadAsyncCall; |
|
286 |
|
287 m_browserFuncs.releasevariantvalue = _NPN_ReleaseVariantValue; |
|
288 m_browserFuncs.getstringidentifier = _NPN_GetStringIdentifier; |
|
289 m_browserFuncs.getstringidentifiers = _NPN_GetStringIdentifiers; |
|
290 m_browserFuncs.getintidentifier = _NPN_GetIntIdentifier; |
|
291 m_browserFuncs.identifierisstring = _NPN_IdentifierIsString; |
|
292 m_browserFuncs.utf8fromidentifier = _NPN_UTF8FromIdentifier; |
|
293 m_browserFuncs.intfromidentifier = _NPN_IntFromIdentifier; |
|
294 m_browserFuncs.createobject = _NPN_CreateObject; |
|
295 m_browserFuncs.retainobject = _NPN_RetainObject; |
|
296 m_browserFuncs.releaseobject = _NPN_ReleaseObject; |
|
297 m_browserFuncs.invoke = _NPN_Invoke; |
|
298 m_browserFuncs.invokeDefault = _NPN_InvokeDefault; |
|
299 m_browserFuncs.evaluate = _NPN_Evaluate; |
|
300 m_browserFuncs.getproperty = _NPN_GetProperty; |
|
301 m_browserFuncs.setproperty = _NPN_SetProperty; |
|
302 m_browserFuncs.removeproperty = _NPN_RemoveProperty; |
|
303 m_browserFuncs.hasproperty = _NPN_HasProperty; |
|
304 m_browserFuncs.hasmethod = _NPN_HasMethod; |
|
305 m_browserFuncs.setexception = _NPN_SetException; |
|
306 m_browserFuncs.enumerate = _NPN_Enumerate; |
|
307 m_browserFuncs.construct = _NPN_Construct; |
|
308 } |
|
309 #endif |
|
310 |
|
311 #if ENABLE(PLUGIN_PACKAGE_SIMPLE_HASH) |
|
312 unsigned PluginPackage::hash() const |
|
313 { |
|
314 unsigned hashCodes[] = { |
|
315 m_path.impl()->hash(), |
|
316 m_lastModified |
|
317 }; |
|
318 |
|
319 return StringImpl::computeHash(reinterpret_cast<UChar*>(hashCodes), sizeof(hashCodes) / sizeof(UChar)); |
|
320 } |
|
321 |
|
322 bool PluginPackage::equal(const PluginPackage& a, const PluginPackage& b) |
|
323 { |
|
324 return a.m_description == b.m_description; |
|
325 } |
|
326 #endif |
|
327 |
|
328 int PluginPackage::compareFileVersion(const PlatformModuleVersion& compareVersion) const |
|
329 { |
|
330 // return -1, 0, or 1 if plug-in version is less than, equal to, or greater than |
|
331 // the passed version |
|
332 |
|
333 #if OS(WINDOWS) |
|
334 if (m_moduleVersion.mostSig != compareVersion.mostSig) |
|
335 return m_moduleVersion.mostSig > compareVersion.mostSig ? 1 : -1; |
|
336 if (m_moduleVersion.leastSig != compareVersion.leastSig) |
|
337 return m_moduleVersion.leastSig > compareVersion.leastSig ? 1 : -1; |
|
338 #else |
|
339 if (m_moduleVersion != compareVersion) |
|
340 return m_moduleVersion > compareVersion ? 1 : -1; |
|
341 #endif |
|
342 |
|
343 return 0; |
|
344 } |
|
345 |
|
346 } |