2a42e30663ce26952a22a795e8baa0667a49ce1b
[WebKit-https.git] / Source / WebKit / win / Plugins / PluginPackage.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 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 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 "config.h"
29 #include "PluginPackage.h"
30
31 #include "PluginDatabase.h"
32 #include "PluginDebug.h"
33 #include "PluginView.h"
34 #include <JavaScriptCore/Completion.h>
35 #include <JavaScriptCore/HeapInlines.h>
36 #include <JavaScriptCore/JSGlobalObject.h>
37 #include <WebCore/IdentifierRep.h>
38 #include <WebCore/MIMETypeRegistry.h>
39 #include <WebCore/NP_jsobject.h>
40 #include <WebCore/Timer.h>
41 #include <WebCore/c_utility.h>
42 #include <WebCore/npruntime_impl.h>
43 #include <WebCore/runtime_root.h>
44 #include <string.h>
45 #include <wtf/text/CString.h>
46
47 namespace WebCore {
48
49 PluginPackage::~PluginPackage()
50 {
51     // This destructor gets called during refresh() if PluginDatabase's
52     // PluginSet hash is already populated, as it removes items from
53     // the hash table. Calling the destructor on a loaded plug-in of
54     // course would cause a crash, so we check to call unload before we
55     // ASSERT.
56     // FIXME: There is probably a better way to fix this.
57     if (!m_loadCount)
58         unloadWithoutShutdown();
59     else
60         unload();
61
62     ASSERT(!m_isLoaded);
63 }
64
65 void PluginPackage::freeLibrarySoon()
66 {
67     ASSERT(!m_freeLibraryTimer.isActive());
68     ASSERT(m_module);
69     ASSERT(!m_loadCount);
70
71     m_freeLibraryTimer.startOneShot(0_s);
72 }
73
74 void PluginPackage::freeLibraryTimerFired()
75 {
76     ASSERT(m_module);
77     // Do nothing if the module got loaded again meanwhile
78     if (!m_loadCount) {
79         ::FreeLibrary(m_module);
80         m_module = 0;
81     }
82 }
83
84
85 int PluginPackage::compare(const PluginPackage& compareTo) const
86 {
87     // Sort plug-ins that allow multiple instances first.
88     bool AallowsMultipleInstances = !quirks().contains(PluginQuirkDontAllowMultipleInstances);
89     bool BallowsMultipleInstances = !compareTo.quirks().contains(PluginQuirkDontAllowMultipleInstances);
90     if (AallowsMultipleInstances != BallowsMultipleInstances)
91         return AallowsMultipleInstances ? -1 : 1;
92
93     // Sort plug-ins in a preferred path first.
94     bool AisInPreferredDirectory = PluginDatabase::isPreferredPluginDirectory(parentDirectory());
95     bool BisInPreferredDirectory = PluginDatabase::isPreferredPluginDirectory(compareTo.parentDirectory());
96     if (AisInPreferredDirectory != BisInPreferredDirectory)
97         return AisInPreferredDirectory ? -1 : 1;
98
99     int diff = strcmp(name().utf8().data(), compareTo.name().utf8().data());
100     if (diff)
101         return diff;
102
103     diff = compareFileVersion(compareTo.version());
104     if (diff)
105         return diff;
106
107     return strcmp(parentDirectory().utf8().data(), compareTo.parentDirectory().utf8().data());
108 }
109
110 PluginPackage::PluginPackage(const String& path, const time_t& lastModified)
111     : m_isEnabled(true)
112     , m_isLoaded(false)
113     , m_loadCount(0)
114     , m_path(path)
115     , m_moduleVersion(0)
116     , m_module(0)
117     , m_lastModified(lastModified)
118     , m_freeLibraryTimer(*this, &PluginPackage::freeLibraryTimerFired)
119 #if ENABLE(NETSCAPE_PLUGIN_METADATA_CACHE)
120     , m_infoIsFromCache(true)
121 #endif
122 {
123     m_fileName = pathGetFileName(m_path);
124     m_parentDirectory = m_path.left(m_path.length() - m_fileName.length() - 1);
125 }
126
127 void PluginPackage::unload()
128 {
129     if (!m_isLoaded)
130         return;
131
132     if (--m_loadCount > 0)
133         return;
134
135 #if ENABLE(NETSCAPE_PLUGIN_API)
136     m_NPP_Shutdown();
137 #endif
138
139     unloadWithoutShutdown();
140 }
141
142 void PluginPackage::unloadWithoutShutdown()
143 {
144     if (!m_isLoaded)
145         return;
146
147     ASSERT(!m_loadCount);
148     ASSERT(m_module);
149
150     // <rdar://5530519>: Crash when closing tab with pdf file (Reader 7 only)
151     // If the plugin has subclassed its parent window, as with Reader 7, we may have
152     // gotten here by way of the plugin's internal window proc forwarding a message to our
153     // original window proc. If we free the plugin library from here, we will jump back
154     // to code we just freed when we return, so delay calling FreeLibrary at least until
155     // the next message loop
156     freeLibrarySoon();
157
158     m_isLoaded = false;
159 }
160
161 void PluginPackage::setEnabled(bool enabled)
162 {
163     m_isEnabled = enabled;
164 }
165
166 RefPtr<PluginPackage> PluginPackage::createPackage(const String& path, const time_t& lastModified)
167 {
168     RefPtr<PluginPackage> package = adoptRef(new PluginPackage(path, lastModified));
169
170     if (!package->fetchInfo())
171         return nullptr;
172
173     return package;
174 }
175
176 #if ENABLE(NETSCAPE_PLUGIN_METADATA_CACHE)
177 Ref<PluginPackage> PluginPackage::createPackageFromCache(const String& path, const time_t& lastModified, const String& name, const String& description, const String& mimeDescription)
178 {
179     Ref<PluginPackage> package = adoptRef(*new PluginPackage(path, lastModified));
180     package->m_name = name;
181     package->m_description = description;
182     package->determineModuleVersionFromDescription();
183     package->setMIMEDescription(mimeDescription);
184     package->m_infoIsFromCache = true;
185     return package;
186 }
187 #endif
188
189 #if ENABLE(NETSCAPE_PLUGIN_API)
190 static void getListFromVariantArgs(JSC::ExecState* exec, const NPVariant* args, unsigned argCount, JSC::Bindings::RootObject* rootObject, JSC::MarkedArgumentBuffer& aList)
191 {
192     for (unsigned i = 0; i < argCount; ++i)
193         aList.append(JSC::Bindings::convertNPVariantToValue(exec, &args[i], rootObject));
194 }
195
196 static bool NPN_Evaluate(NPP instance, NPObject* o, NPString* s, NPVariant* variant)
197 {
198     if (o->_class == NPScriptObjectClass) {
199         JavaScriptObject* obj = reinterpret_cast<JavaScriptObject*>(o);
200
201         JSC::Bindings::RootObject* rootObject = obj->rootObject;
202         if (!rootObject || !rootObject->isValid())
203             return false;
204
205         // There is a crash in Flash when evaluating a script that destroys the
206         // PluginView, so we destroy it asynchronously.
207         PluginView::keepAlive(instance);
208
209         auto globalObject = rootObject->globalObject();
210         auto& vm = globalObject->vm();
211         JSC::JSLockHolder lock(vm);
212         auto scope = DECLARE_CATCH_SCOPE(vm);
213
214         JSC::ExecState* exec = globalObject->globalExec();
215         String scriptString = JSC::Bindings::convertNPStringToUTF16(s);
216
217         JSC::JSValue returnValue = JSC::evaluate(exec, JSC::makeSource(scriptString, { }), JSC::JSValue());
218
219         JSC::Bindings::convertValueToNPVariant(exec, returnValue, variant);
220         scope.clearException();
221         return true;
222     }
223
224     VOID_TO_NPVARIANT(*variant);
225     return false;
226 }
227
228 static bool NPN_Invoke(NPP npp, NPObject* o, NPIdentifier methodName, const NPVariant* args, uint32_t argCount, NPVariant* result)
229 {
230     if (o->_class == NPScriptObjectClass) {
231         JavaScriptObject* obj = reinterpret_cast<JavaScriptObject*>(o);
232
233         IdentifierRep* i = static_cast<IdentifierRep*>(methodName);
234         if (!i->isString())
235             return false;
236
237         // Special case the "eval" method.
238         if (methodName == _NPN_GetStringIdentifier("eval")) {
239             if (argCount != 1)
240                 return false;
241             if (args[0].type != NPVariantType_String)
242                 return false;
243             return WebCore::NPN_Evaluate(npp, o, const_cast<NPString*>(&args[0].value.stringValue), result);
244         }
245
246         // Look up the function object.
247         JSC::Bindings::RootObject* rootObject = obj->rootObject;
248         if (!rootObject || !rootObject->isValid())
249             return false;
250
251         auto globalObject = rootObject->globalObject();
252         auto& vm = globalObject->vm();
253         JSC::JSLockHolder lock(vm);
254         auto scope = DECLARE_CATCH_SCOPE(vm);
255
256         JSC::ExecState* exec = globalObject->globalExec();
257         JSC::JSValue function = obj->imp->get(exec, JSC::Bindings::identifierFromNPIdentifier(exec, i->string()));
258         JSC::CallData callData;
259         JSC::CallType callType = getCallData(function, callData);
260         if (callType == JSC::CallType::None)
261             return false;
262
263         // Call the function object.
264         JSC::MarkedArgumentBuffer argList;
265         getListFromVariantArgs(exec, args, argCount, rootObject, argList);
266         JSC::JSValue resultV = JSC::call(exec, function, callType, callData, obj->imp, argList);
267
268         // Convert and return the result of the function call.
269         JSC::Bindings::convertValueToNPVariant(exec, resultV, result);
270         scope.clearException();
271         return true;
272     }
273
274     if (o->_class->invoke)
275         return o->_class->invoke(o, methodName, args, argCount, result);
276
277     VOID_TO_NPVARIANT(*result);
278     return true;
279 }
280
281 void PluginPackage::initializeBrowserFuncs()
282 {
283     memset(&m_browserFuncs, 0, sizeof(m_browserFuncs));
284     m_browserFuncs.size = sizeof(m_browserFuncs);
285     m_browserFuncs.version = NPVersion();
286
287     m_browserFuncs.geturl = NPN_GetURL;
288     m_browserFuncs.posturl = NPN_PostURL;
289     m_browserFuncs.requestread = NPN_RequestRead;
290     m_browserFuncs.newstream = NPN_NewStream;
291     m_browserFuncs.write = NPN_Write;
292     m_browserFuncs.destroystream = NPN_DestroyStream;
293     m_browserFuncs.status = NPN_Status;
294     m_browserFuncs.uagent = NPN_UserAgent;
295     m_browserFuncs.memalloc = NPN_MemAlloc;
296     m_browserFuncs.memfree = NPN_MemFree;
297     m_browserFuncs.memflush = NPN_MemFlush;
298     m_browserFuncs.reloadplugins = NPN_ReloadPlugins;
299     m_browserFuncs.geturlnotify = NPN_GetURLNotify;
300     m_browserFuncs.posturlnotify = NPN_PostURLNotify;
301     m_browserFuncs.getvalue = NPN_GetValue;
302     m_browserFuncs.setvalue = NPN_SetValue;
303     m_browserFuncs.invalidaterect = NPN_InvalidateRect;
304     m_browserFuncs.invalidateregion = NPN_InvalidateRegion;
305     m_browserFuncs.forceredraw = NPN_ForceRedraw;
306     m_browserFuncs.getJavaEnv = NPN_GetJavaEnv;
307     m_browserFuncs.getJavaPeer = NPN_GetJavaPeer;
308     m_browserFuncs.pushpopupsenabledstate = NPN_PushPopupsEnabledState;
309     m_browserFuncs.poppopupsenabledstate = NPN_PopPopupsEnabledState;
310     m_browserFuncs.pluginthreadasynccall = NPN_PluginThreadAsyncCall;
311
312     m_browserFuncs.releasevariantvalue = _NPN_ReleaseVariantValue;
313     m_browserFuncs.getstringidentifier = _NPN_GetStringIdentifier;
314     m_browserFuncs.getstringidentifiers = _NPN_GetStringIdentifiers;
315     m_browserFuncs.getintidentifier = _NPN_GetIntIdentifier;
316     m_browserFuncs.identifierisstring = _NPN_IdentifierIsString;
317     m_browserFuncs.utf8fromidentifier = _NPN_UTF8FromIdentifier;
318     m_browserFuncs.intfromidentifier = _NPN_IntFromIdentifier;
319     m_browserFuncs.createobject = _NPN_CreateObject;
320     m_browserFuncs.retainobject = _NPN_RetainObject;
321     m_browserFuncs.releaseobject = _NPN_ReleaseObject;
322     m_browserFuncs.invoke = WebCore::NPN_Invoke;
323     m_browserFuncs.invokeDefault = _NPN_InvokeDefault;
324     m_browserFuncs.evaluate = WebCore::NPN_Evaluate;
325     m_browserFuncs.getproperty = _NPN_GetProperty;
326     m_browserFuncs.setproperty = _NPN_SetProperty;
327     m_browserFuncs.removeproperty = _NPN_RemoveProperty;
328     m_browserFuncs.hasproperty = _NPN_HasProperty;
329     m_browserFuncs.hasmethod = _NPN_HasMethod;
330     m_browserFuncs.setexception = _NPN_SetException;
331     m_browserFuncs.enumerate = _NPN_Enumerate;
332     m_browserFuncs.construct = _NPN_Construct;
333     m_browserFuncs.getvalueforurl = NPN_GetValueForURL;
334     m_browserFuncs.setvalueforurl = NPN_SetValueForURL;
335     m_browserFuncs.getauthenticationinfo = NPN_GetAuthenticationInfo;
336
337     m_browserFuncs.popupcontextmenu = NPN_PopUpContextMenu;
338 }
339 #endif // ENABLE(NETSCAPE_PLUGIN_API)
340
341 int PluginPackage::compareFileVersion(const PlatformModuleVersion& compareVersion) const
342 {
343     // return -1, 0, or 1 if plug-in version is less than, equal to, or greater than
344     // the passed version
345
346     if (m_moduleVersion.mostSig != compareVersion.mostSig)
347         return m_moduleVersion.mostSig > compareVersion.mostSig ? 1 : -1;
348     if (m_moduleVersion.leastSig != compareVersion.leastSig)
349         return m_moduleVersion.leastSig > compareVersion.leastSig ? 1 : -1;
350
351     return 0;
352 }
353
354 #if ENABLE(NETSCAPE_PLUGIN_METADATA_CACHE)
355 bool PluginPackage::ensurePluginLoaded()
356 {
357     if (!m_infoIsFromCache)
358         return m_isLoaded;
359
360     m_quirks = PluginQuirkSet();
361     m_name = String();
362     m_description = String();
363     m_fullMIMEDescription = String();
364     m_moduleVersion = 0;
365
366     return fetchInfo();
367 }
368 #endif
369
370 }