1 /*
2 * Copyright (C) 2006, 2008 Apple Inc. All rights reserved.
3 * Copyright (C) 2008 Collabora Ltd. All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
8 * 1. Redistributions of source code must retain the above copyright
9 * notice, this list of conditions and the following disclaimer.
10 * 2. Redistributions in binary form must reproduce the above copyright
11 * notice, this list of conditions and the following disclaimer in the
12 * documentation and/or other materials provided with the distribution.
13 *
14 * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY
15 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
17 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR
18 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
19 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
20 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
21 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
22 * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
23 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
24 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
25 */
26
27 #include "config.h"
28 #include "PluginPackage.h"
29
30 #include "CString.h"
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
39 namespace WebCore {
40
~PluginPackage()41 PluginPackage::~PluginPackage()
42 {
43 // This destructor gets called during refresh() if PluginDatabase's
44 // PluginSet hash is already populated, as it removes items from
45 // the hash table. Calling the destructor on a loaded plug-in of
46 // course would cause a crash, so we check to call unload before we
47 // ASSERT.
48 // FIXME: There is probably a better way to fix this.
49 if (m_loadCount == 0)
50 unloadWithoutShutdown();
51 else
52 unload();
53
54 ASSERT(!m_isLoaded);
55 }
56
freeLibrarySoon()57 void PluginPackage::freeLibrarySoon()
58 {
59 ASSERT(!m_freeLibraryTimer.isActive());
60 ASSERT(m_module);
61 ASSERT(m_loadCount == 0);
62
63 #ifdef ANDROID_PLUGINS
64 // TODO(jripley): Timer<T> is broken. Unload immediately for now.
65 unloadModule(m_module);
66 m_module = 0;
67 #else
68 m_freeLibraryTimer.startOneShot(0);
69 #endif
70 }
71
freeLibraryTimerFired(Timer<PluginPackage> *)72 void PluginPackage::freeLibraryTimerFired(Timer<PluginPackage>*)
73 {
74 ASSERT(m_module);
75 ASSERT(m_loadCount == 0);
76
77 unloadModule(m_module);
78 m_module = 0;
79 }
80
81
compare(const PluginPackage & compareTo) const82 int PluginPackage::compare(const PluginPackage& compareTo) const
83 {
84 // Sort plug-ins that allow multiple instances first.
85 bool AallowsMultipleInstances = !quirks().contains(PluginQuirkDontAllowMultipleInstances);
86 bool BallowsMultipleInstances = !compareTo.quirks().contains(PluginQuirkDontAllowMultipleInstances);
87 if (AallowsMultipleInstances != BallowsMultipleInstances)
88 return AallowsMultipleInstances ? -1 : 1;
89
90 // Sort plug-ins in a preferred path first.
91 bool AisInPreferredDirectory = PluginDatabase::isPreferredPluginDirectory(parentDirectory());
92 bool BisInPreferredDirectory = PluginDatabase::isPreferredPluginDirectory(compareTo.parentDirectory());
93 if (AisInPreferredDirectory != BisInPreferredDirectory)
94 return AisInPreferredDirectory ? -1 : 1;
95
96 int diff = strcmp(name().utf8().data(), compareTo.name().utf8().data());
97 if (diff)
98 return diff;
99
100 if (diff = compareFileVersion(compareTo.version()))
101 return diff;
102
103 return strcmp(parentDirectory().utf8().data(), compareTo.parentDirectory().utf8().data());
104 }
105
PluginPackage(const String & path,const time_t & lastModified)106 PluginPackage::PluginPackage(const String& path, const time_t& lastModified)
107 : m_isLoaded(false)
108 , m_loadCount(0)
109 , m_path(path)
110 , m_moduleVersion(0)
111 , m_module(0)
112 , m_lastModified(lastModified)
113 , m_freeLibraryTimer(this, &PluginPackage::freeLibraryTimerFired)
114 {
115 m_fileName = pathGetFileName(m_path);
116 m_parentDirectory = m_path.left(m_path.length() - m_fileName.length() - 1);
117 }
118
unload()119 void PluginPackage::unload()
120 {
121 if (!m_isLoaded)
122 return;
123
124 if (--m_loadCount > 0)
125 return;
126
127 m_NPP_Shutdown();
128
129 unloadWithoutShutdown();
130 }
131
unloadWithoutShutdown()132 void PluginPackage::unloadWithoutShutdown()
133 {
134 if (!m_isLoaded)
135 return;
136
137 ASSERT(m_loadCount == 0);
138 ASSERT(m_module);
139
140 #if defined(ANDROID_PLUGINS)
141 // Remove the Java object from PluginList.
142 unregisterPluginObject();
143 #endif
144
145 // <rdar://5530519>: Crash when closing tab with pdf file (Reader 7 only)
146 // If the plugin has subclassed its parent window, as with Reader 7, we may have
147 // gotten here by way of the plugin's internal window proc forwarding a message to our
148 // original window proc. If we free the plugin library from here, we will jump back
149 // to code we just freed when we return, so delay calling FreeLibrary at least until
150 // the next message loop
151 freeLibrarySoon();
152
153 m_isLoaded = false;
154 }
155
createPackage(const String & path,const time_t & lastModified)156 PassRefPtr<PluginPackage> PluginPackage::createPackage(const String& path, const time_t& lastModified)
157 {
158 RefPtr<PluginPackage> package = adoptRef(new PluginPackage(path, lastModified));
159
160 if (!package->fetchInfo())
161 return 0;
162
163 return package.release();
164 }
165
166 #if defined(XP_UNIX)
determineQuirks(const String & mimeType)167 void PluginPackage::determineQuirks(const String& mimeType)
168 {
169 if (MIMETypeRegistry::isJavaAppletMIMEType(mimeType)) {
170 // Because a single process cannot create multiple VMs, and we cannot reliably unload a
171 // Java VM, we cannot unload the Java plugin, or we'll lose reference to our only VM
172 m_quirks.add(PluginQuirkDontUnloadPlugin);
173
174 // Setting the window region to an empty region causes bad scrolling repaint problems
175 // with the Java plug-in.
176 m_quirks.add(PluginQuirkDontClipToZeroRectWhenScrolling);
177 return;
178 }
179
180 if (mimeType == "application/x-shockwave-flash") {
181 static const PlatformModuleVersion flashTenVersion(0x0a000000);
182
183 if (compareFileVersion(flashTenVersion) >= 0) {
184 // Flash 10.0 b218 doesn't like having a NULL window handle
185 m_quirks.add(PluginQuirkDontSetNullWindowHandleOnDestroy);
186 #if PLATFORM(QT)
187 m_quirks.add(PluginQuirkRequiresGtkToolKit);
188 #endif
189 } else {
190 // Flash 9 and older requests windowless plugins if we return a mozilla user agent
191 m_quirks.add(PluginQuirkWantsMozillaUserAgent);
192 }
193
194 m_quirks.add(PluginQuirkThrottleInvalidate);
195 m_quirks.add(PluginQuirkThrottleWMUserPlusOneMessages);
196 m_quirks.add(PluginQuirkFlashURLNotifyBug);
197 }
198 }
199 #endif
200
201 #if !PLATFORM(WIN_OS)
determineModuleVersionFromDescription()202 void PluginPackage::determineModuleVersionFromDescription()
203 {
204 // It's a bit lame to detect the plugin version by parsing it
205 // from the plugin description string, but it doesn't seem that
206 // version information is available in any standardized way at
207 // the module level, like in Windows
208
209 if (m_description.isEmpty())
210 return;
211
212 if (m_description.startsWith("Shockwave Flash") && m_description.length() >= 19) {
213 // The flash version as a PlatformModuleVersion differs on Unix from Windows
214 // since the revision can be larger than a 8 bits, so we allow it 16 here and
215 // push the major/minor up 8 bits. Thus on Unix, Flash's version may be
216 // 0x0a000000 instead of 0x000a0000.
217
218 Vector<String> versionParts;
219 m_description.substring(16).split(' ', /*allowEmptyEntries =*/ false, versionParts);
220 if (versionParts.isEmpty())
221 return;
222
223 if (versionParts.size() >= 1) {
224 Vector<String> majorMinorParts;
225 versionParts[0].split('.', majorMinorParts);
226 if (majorMinorParts.size() >= 1) {
227 bool converted = false;
228 unsigned major = majorMinorParts[0].toUInt(&converted);
229 if (converted)
230 m_moduleVersion = (major & 0xff) << 24;
231 }
232 if (majorMinorParts.size() == 2) {
233 bool converted = false;
234 unsigned minor = majorMinorParts[1].toUInt(&converted);
235 if (converted)
236 m_moduleVersion |= (minor & 0xff) << 16;
237 }
238 }
239
240 if (versionParts.size() >= 2) {
241 String revision = versionParts[1];
242 if (revision.length() > 1 && (revision[0] == 'r' || revision[0] == 'b')) {
243 revision.remove(0, 1);
244 m_moduleVersion |= revision.toInt() & 0xffff;
245 }
246 }
247 }
248 }
249 #endif
250
251 }
252