1 /*
2  * Copyright (C) 2004, 2006 Apple Computer, Inc.  All rights reserved.
3  *
4  * Redistribution and use in source and binary forms, with or without
5  * modification, are permitted provided that the following conditions
6  * are met:
7  * 1. Redistributions of source code must retain the above copyright
8  *    notice, this list of conditions and the following disclaimer.
9  * 2. Redistributions in binary form must reproduce the above copyright
10  *    notice, this list of conditions and the following disclaimer in the
11  *    documentation and/or other materials provided with the distribution.
12  *
13  * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY
14  * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
15  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
16  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL APPLE COMPUTER, INC. OR
17  * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
18  * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
19  * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
20  * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
21  * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
22  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
23  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
24  */
25 
26 #include "config.h"
27 
28 #if ENABLE(NETSCAPE_PLUGIN_API)
29 
30 #include "NP_jsobject.h"
31 
32 #include "PlatformString.h"
33 #include "PluginView.h"
34 #include "StringSourceProvider.h"
35 #include "c_utility.h"
36 #include "c_instance.h"
37 #include "IdentifierRep.h"
38 #include "JSDOMBinding.h"
39 #include "npruntime_impl.h"
40 #include "npruntime_priv.h"
41 #include "runtime_root.h"
42 #include <runtime/Error.h>
43 #include <runtime/JSGlobalObject.h>
44 #include <runtime/JSLock.h>
45 #include <runtime/PropertyNameArray.h>
46 #include <parser/SourceCode.h>
47 #include <runtime/Completion.h>
48 #include <runtime/Completion.h>
49 
50 using namespace JSC;
51 using namespace JSC::Bindings;
52 using namespace WebCore;
53 
54 class ObjectMap {
55 public:
get(RootObject * rootObject,JSObject * jsObject)56     NPObject* get(RootObject* rootObject, JSObject* jsObject)
57     {
58         return m_map.get(rootObject).get(jsObject);
59     }
60 
add(RootObject * rootObject,JSObject * jsObject,NPObject * npObject)61     void add(RootObject* rootObject, JSObject* jsObject, NPObject* npObject)
62     {
63         HashMap<RootObject*, JSToNPObjectMap>::iterator iter = m_map.find(rootObject);
64         if (iter == m_map.end()) {
65             rootObject->addInvalidationCallback(&m_invalidationCallback);
66             iter = m_map.add(rootObject, JSToNPObjectMap()).first;
67         }
68 
69         ASSERT(iter->second.find(jsObject) == iter->second.end());
70         iter->second.add(jsObject, npObject);
71     }
72 
remove(RootObject * rootObject)73     void remove(RootObject* rootObject)
74     {
75         HashMap<RootObject*, JSToNPObjectMap>::iterator iter = m_map.find(rootObject);
76         ASSERT(iter != m_map.end());
77         m_map.remove(iter);
78     }
79 
remove(RootObject * rootObject,JSObject * jsObject)80     void remove(RootObject* rootObject, JSObject* jsObject)
81     {
82         HashMap<RootObject*, JSToNPObjectMap>::iterator iter = m_map.find(rootObject);
83         ASSERT(iter != m_map.end());
84         ASSERT(iter->second.find(jsObject) != iter->second.end());
85 
86         iter->second.remove(jsObject);
87     }
88 
89 private:
90     struct RootObjectInvalidationCallback : public RootObject::InvalidationCallback {
91         virtual void operator()(RootObject*);
92     };
93     RootObjectInvalidationCallback m_invalidationCallback;
94 
95     // JSObjects are protected by RootObject.
96     typedef HashMap<JSObject*, NPObject*> JSToNPObjectMap;
97     HashMap<RootObject*, JSToNPObjectMap> m_map;
98 };
99 
100 
objectMap()101 static ObjectMap& objectMap()
102 {
103     DEFINE_STATIC_LOCAL(ObjectMap, map, ());
104     return map;
105 }
106 
operator ()(RootObject * rootObject)107 void ObjectMap::RootObjectInvalidationCallback::operator()(RootObject* rootObject)
108 {
109     objectMap().remove(rootObject);
110 }
111 
getListFromVariantArgs(ExecState * exec,const NPVariant * args,unsigned argCount,RootObject * rootObject,MarkedArgumentBuffer & aList)112 static void getListFromVariantArgs(ExecState* exec, const NPVariant* args, unsigned argCount, RootObject* rootObject, MarkedArgumentBuffer& aList)
113 {
114     for (unsigned i = 0; i < argCount; ++i)
115         aList.append(convertNPVariantToValue(exec, &args[i], rootObject));
116 }
117 
jsAllocate(NPP,NPClass *)118 static NPObject* jsAllocate(NPP, NPClass*)
119 {
120     return static_cast<NPObject*>(malloc(sizeof(JavaScriptObject)));
121 }
122 
jsDeallocate(NPObject * npObj)123 static void jsDeallocate(NPObject* npObj)
124 {
125     JavaScriptObject* obj = reinterpret_cast<JavaScriptObject*>(npObj);
126 
127     if (obj->rootObject && obj->rootObject->isValid()) {
128         objectMap().remove(obj->rootObject, obj->imp);
129         obj->rootObject->gcUnprotect(obj->imp);
130     }
131 
132     if (obj->rootObject)
133         obj->rootObject->deref();
134 
135     free(obj);
136 }
137 
138 static NPClass javascriptClass = { 1, jsAllocate, jsDeallocate, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 };
139 static NPClass noScriptClass = { 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 };
140 
141 NPClass* NPScriptObjectClass = &javascriptClass;
142 static NPClass* NPNoScriptObjectClass = &noScriptClass;
143 
_NPN_CreateScriptObject(NPP npp,JSObject * imp,PassRefPtr<RootObject> rootObject)144 NPObject* _NPN_CreateScriptObject(NPP npp, JSObject* imp, PassRefPtr<RootObject> rootObject)
145 {
146     if (NPObject* object = objectMap().get(rootObject.get(), imp))
147         return _NPN_RetainObject(object);
148 
149     JavaScriptObject* obj = reinterpret_cast<JavaScriptObject*>(_NPN_CreateObject(npp, NPScriptObjectClass));
150 
151     obj->rootObject = rootObject.releaseRef();
152 
153     if (obj->rootObject) {
154         obj->rootObject->gcProtect(imp);
155         objectMap().add(obj->rootObject, imp, reinterpret_cast<NPObject*>(obj));
156     }
157 
158     obj->imp = imp;
159 
160     return reinterpret_cast<NPObject*>(obj);
161 }
162 
_NPN_CreateNoScriptObject(void)163 NPObject* _NPN_CreateNoScriptObject(void)
164 {
165     return _NPN_CreateObject(0, NPNoScriptObjectClass);
166 }
167 
_NPN_InvokeDefault(NPP,NPObject * o,const NPVariant * args,uint32_t argCount,NPVariant * result)168 bool _NPN_InvokeDefault(NPP, NPObject* o, const NPVariant* args, uint32_t argCount, NPVariant* result)
169 {
170     if (o->_class == NPScriptObjectClass) {
171         JavaScriptObject* obj = reinterpret_cast<JavaScriptObject*>(o);
172 
173         VOID_TO_NPVARIANT(*result);
174 
175         // Lookup the function object.
176         RootObject* rootObject = obj->rootObject;
177         if (!rootObject || !rootObject->isValid())
178             return false;
179 
180         ExecState* exec = rootObject->globalObject()->globalExec();
181         JSLock lock(SilenceAssertionsOnly);
182 
183         // Call the function object.
184         JSValue function = obj->imp;
185         CallData callData;
186         CallType callType = getCallData(function, callData);
187         if (callType == CallTypeNone)
188             return false;
189 
190         MarkedArgumentBuffer argList;
191         getListFromVariantArgs(exec, args, argCount, rootObject, argList);
192         RefPtr<JSGlobalData> globalData(&exec->globalData());
193         globalData->timeoutChecker.start();
194         JSValue resultV = JSC::call(exec, function, callType, callData, function, argList);
195         globalData->timeoutChecker.stop();
196 
197         // Convert and return the result of the function call.
198         convertValueToNPVariant(exec, resultV, result);
199         exec->clearException();
200         return true;
201     }
202 
203     if (o->_class->invokeDefault)
204         return o->_class->invokeDefault(o, args, argCount, result);
205     VOID_TO_NPVARIANT(*result);
206     return true;
207 }
208 
_NPN_Invoke(NPP npp,NPObject * o,NPIdentifier methodName,const NPVariant * args,uint32_t argCount,NPVariant * result)209 bool _NPN_Invoke(NPP npp, NPObject* o, NPIdentifier methodName, const NPVariant* args, uint32_t argCount, NPVariant* result)
210 {
211     if (o->_class == NPScriptObjectClass) {
212         JavaScriptObject* obj = reinterpret_cast<JavaScriptObject*>(o);
213 
214         IdentifierRep* i = static_cast<IdentifierRep*>(methodName);
215         if (!i->isString())
216             return false;
217 
218         // Special case the "eval" method.
219         if (methodName == _NPN_GetStringIdentifier("eval")) {
220             if (argCount != 1)
221                 return false;
222             if (args[0].type != NPVariantType_String)
223                 return false;
224             return _NPN_Evaluate(npp, o, const_cast<NPString*>(&args[0].value.stringValue), result);
225         }
226 
227         // Look up the function object.
228         RootObject* rootObject = obj->rootObject;
229         if (!rootObject || !rootObject->isValid())
230             return false;
231         ExecState* exec = rootObject->globalObject()->globalExec();
232         JSLock lock(SilenceAssertionsOnly);
233         JSValue function = obj->imp->get(exec, identifierFromNPIdentifier(exec, i->string()));
234         CallData callData;
235         CallType callType = getCallData(function, callData);
236         if (callType == CallTypeNone)
237             return false;
238 
239         // Call the function object.
240         MarkedArgumentBuffer argList;
241         getListFromVariantArgs(exec, args, argCount, rootObject, argList);
242         RefPtr<JSGlobalData> globalData(&exec->globalData());
243         globalData->timeoutChecker.start();
244         JSValue resultV = JSC::call(exec, function, callType, callData, obj->imp, argList);
245         globalData->timeoutChecker.stop();
246 
247         // Convert and return the result of the function call.
248         convertValueToNPVariant(exec, resultV, result);
249         exec->clearException();
250         return true;
251     }
252 
253     if (o->_class->invoke)
254         return o->_class->invoke(o, methodName, args, argCount, result);
255 
256     VOID_TO_NPVARIANT(*result);
257     return true;
258 }
259 
_NPN_Evaluate(NPP instance,NPObject * o,NPString * s,NPVariant * variant)260 bool _NPN_Evaluate(NPP instance, NPObject* o, NPString* s, NPVariant* variant)
261 {
262     if (o->_class == NPScriptObjectClass) {
263         JavaScriptObject* obj = reinterpret_cast<JavaScriptObject*>(o);
264 
265         RootObject* rootObject = obj->rootObject;
266         if (!rootObject || !rootObject->isValid())
267             return false;
268 
269         // There is a crash in Flash when evaluating a script that destroys the
270         // PluginView, so we destroy it asynchronously.
271         PluginView::keepAlive(instance);
272 
273         ExecState* exec = rootObject->globalObject()->globalExec();
274         JSLock lock(SilenceAssertionsOnly);
275         String scriptString = convertNPStringToUTF16(s);
276         RefPtr<JSGlobalData> globalData(&exec->globalData());
277         globalData->timeoutChecker.start();
278         Completion completion = JSC::evaluate(rootObject->globalObject()->globalExec(), rootObject->globalObject()->globalScopeChain(), makeSource(scriptString), JSC::JSValue());
279         globalData->timeoutChecker.stop();
280         ComplType type = completion.complType();
281 
282         JSValue result;
283         if (type == Normal) {
284             result = completion.value();
285             if (!result)
286                 result = jsUndefined();
287         } else
288             result = jsUndefined();
289 
290         convertValueToNPVariant(exec, result, variant);
291         exec->clearException();
292         return true;
293     }
294 
295     VOID_TO_NPVARIANT(*variant);
296     return false;
297 }
298 
_NPN_GetProperty(NPP,NPObject * o,NPIdentifier propertyName,NPVariant * variant)299 bool _NPN_GetProperty(NPP, NPObject* o, NPIdentifier propertyName, NPVariant* variant)
300 {
301     if (o->_class == NPScriptObjectClass) {
302         JavaScriptObject* obj = reinterpret_cast<JavaScriptObject*>(o);
303 
304         RootObject* rootObject = obj->rootObject;
305         if (!rootObject || !rootObject->isValid())
306             return false;
307 
308         ExecState* exec = rootObject->globalObject()->globalExec();
309         IdentifierRep* i = static_cast<IdentifierRep*>(propertyName);
310 
311         JSLock lock(SilenceAssertionsOnly);
312         JSValue result;
313         if (i->isString())
314             result = obj->imp->get(exec, identifierFromNPIdentifier(exec, i->string()));
315         else
316             result = obj->imp->get(exec, i->number());
317 
318         convertValueToNPVariant(exec, result, variant);
319         exec->clearException();
320         return true;
321     }
322 
323     if (o->_class->hasProperty && o->_class->getProperty) {
324         if (o->_class->hasProperty(o, propertyName))
325             return o->_class->getProperty(o, propertyName, variant);
326         return false;
327     }
328 
329     VOID_TO_NPVARIANT(*variant);
330     return false;
331 }
332 
_NPN_SetProperty(NPP,NPObject * o,NPIdentifier propertyName,const NPVariant * variant)333 bool _NPN_SetProperty(NPP, NPObject* o, NPIdentifier propertyName, const NPVariant* variant)
334 {
335     if (o->_class == NPScriptObjectClass) {
336         JavaScriptObject* obj = reinterpret_cast<JavaScriptObject*>(o);
337 
338         RootObject* rootObject = obj->rootObject;
339         if (!rootObject || !rootObject->isValid())
340             return false;
341 
342         ExecState* exec = rootObject->globalObject()->globalExec();
343         JSLock lock(SilenceAssertionsOnly);
344         IdentifierRep* i = static_cast<IdentifierRep*>(propertyName);
345 
346         if (i->isString()) {
347             PutPropertySlot slot;
348             obj->imp->put(exec, identifierFromNPIdentifier(exec, i->string()), convertNPVariantToValue(exec, variant, rootObject), slot);
349         } else
350             obj->imp->put(exec, i->number(), convertNPVariantToValue(exec, variant, rootObject));
351         exec->clearException();
352         return true;
353     }
354 
355     if (o->_class->setProperty)
356         return o->_class->setProperty(o, propertyName, variant);
357 
358     return false;
359 }
360 
_NPN_RemoveProperty(NPP,NPObject * o,NPIdentifier propertyName)361 bool _NPN_RemoveProperty(NPP, NPObject* o, NPIdentifier propertyName)
362 {
363     if (o->_class == NPScriptObjectClass) {
364         JavaScriptObject* obj = reinterpret_cast<JavaScriptObject*>(o);
365 
366         RootObject* rootObject = obj->rootObject;
367         if (!rootObject || !rootObject->isValid())
368             return false;
369 
370         ExecState* exec = rootObject->globalObject()->globalExec();
371         IdentifierRep* i = static_cast<IdentifierRep*>(propertyName);
372         if (i->isString()) {
373             if (!obj->imp->hasProperty(exec, identifierFromNPIdentifier(exec, i->string()))) {
374                 exec->clearException();
375                 return false;
376             }
377         } else {
378             if (!obj->imp->hasProperty(exec, i->number())) {
379                 exec->clearException();
380                 return false;
381             }
382         }
383 
384         JSLock lock(SilenceAssertionsOnly);
385         if (i->isString())
386             obj->imp->deleteProperty(exec, identifierFromNPIdentifier(exec, i->string()));
387         else
388             obj->imp->deleteProperty(exec, i->number());
389 
390         exec->clearException();
391         return true;
392     }
393     return false;
394 }
395 
_NPN_HasProperty(NPP,NPObject * o,NPIdentifier propertyName)396 bool _NPN_HasProperty(NPP, NPObject* o, NPIdentifier propertyName)
397 {
398     if (o->_class == NPScriptObjectClass) {
399         JavaScriptObject* obj = reinterpret_cast<JavaScriptObject*>(o);
400 
401         RootObject* rootObject = obj->rootObject;
402         if (!rootObject || !rootObject->isValid())
403             return false;
404 
405         ExecState* exec = rootObject->globalObject()->globalExec();
406         IdentifierRep* i = static_cast<IdentifierRep*>(propertyName);
407         JSLock lock(SilenceAssertionsOnly);
408         if (i->isString()) {
409             bool result = obj->imp->hasProperty(exec, identifierFromNPIdentifier(exec, i->string()));
410             exec->clearException();
411             return result;
412         }
413 
414         bool result = obj->imp->hasProperty(exec, i->number());
415         exec->clearException();
416         return result;
417     }
418 
419     if (o->_class->hasProperty)
420         return o->_class->hasProperty(o, propertyName);
421 
422     return false;
423 }
424 
_NPN_HasMethod(NPP,NPObject * o,NPIdentifier methodName)425 bool _NPN_HasMethod(NPP, NPObject* o, NPIdentifier methodName)
426 {
427     if (o->_class == NPScriptObjectClass) {
428         JavaScriptObject* obj = reinterpret_cast<JavaScriptObject*>(o);
429 
430         IdentifierRep* i = static_cast<IdentifierRep*>(methodName);
431         if (!i->isString())
432             return false;
433 
434         RootObject* rootObject = obj->rootObject;
435         if (!rootObject || !rootObject->isValid())
436             return false;
437 
438         ExecState* exec = rootObject->globalObject()->globalExec();
439         JSLock lock(SilenceAssertionsOnly);
440         JSValue func = obj->imp->get(exec, identifierFromNPIdentifier(exec, i->string()));
441         exec->clearException();
442         return !func.isUndefined();
443     }
444 
445     if (o->_class->hasMethod)
446         return o->_class->hasMethod(o, methodName);
447 
448     return false;
449 }
450 
_NPN_SetException(NPObject *,const NPUTF8 * message)451 void _NPN_SetException(NPObject*, const NPUTF8* message)
452 {
453     // Ignoring the NPObject param is consistent with the Mozilla implementation.
454     UString exception(message);
455     CInstance::setGlobalException(exception);
456 }
457 
_NPN_Enumerate(NPP,NPObject * o,NPIdentifier ** identifier,uint32_t * count)458 bool _NPN_Enumerate(NPP, NPObject* o, NPIdentifier** identifier, uint32_t* count)
459 {
460     if (o->_class == NPScriptObjectClass) {
461         JavaScriptObject* obj = reinterpret_cast<JavaScriptObject*>(o);
462 
463         RootObject* rootObject = obj->rootObject;
464         if (!rootObject || !rootObject->isValid())
465             return false;
466 
467         ExecState* exec = rootObject->globalObject()->globalExec();
468         JSLock lock(SilenceAssertionsOnly);
469         PropertyNameArray propertyNames(exec);
470 
471         obj->imp->getPropertyNames(exec, propertyNames);
472         unsigned size = static_cast<unsigned>(propertyNames.size());
473         // FIXME: This should really call NPN_MemAlloc but that's in WebKit
474         NPIdentifier* identifiers = static_cast<NPIdentifier*>(malloc(sizeof(NPIdentifier) * size));
475 
476         for (unsigned i = 0; i < size; ++i)
477             identifiers[i] = _NPN_GetStringIdentifier(propertyNames[i].ustring().utf8().data());
478 
479         *identifier = identifiers;
480         *count = size;
481 
482         exec->clearException();
483         return true;
484     }
485 
486     if (NP_CLASS_STRUCT_VERSION_HAS_ENUM(o->_class) && o->_class->enumerate)
487         return o->_class->enumerate(o, identifier, count);
488 
489     return false;
490 }
491 
_NPN_Construct(NPP,NPObject * o,const NPVariant * args,uint32_t argCount,NPVariant * result)492 bool _NPN_Construct(NPP, NPObject* o, const NPVariant* args, uint32_t argCount, NPVariant* result)
493 {
494     if (o->_class == NPScriptObjectClass) {
495         JavaScriptObject* obj = reinterpret_cast<JavaScriptObject*>(o);
496 
497         VOID_TO_NPVARIANT(*result);
498 
499         // Lookup the constructor object.
500         RootObject* rootObject = obj->rootObject;
501         if (!rootObject || !rootObject->isValid())
502             return false;
503 
504         ExecState* exec = rootObject->globalObject()->globalExec();
505         JSLock lock(SilenceAssertionsOnly);
506 
507         // Call the constructor object.
508         JSValue constructor = obj->imp;
509         ConstructData constructData;
510         ConstructType constructType = getConstructData(constructor, constructData);
511         if (constructType == ConstructTypeNone)
512             return false;
513 
514         MarkedArgumentBuffer argList;
515         getListFromVariantArgs(exec, args, argCount, rootObject, argList);
516         RefPtr<JSGlobalData> globalData(&exec->globalData());
517         globalData->timeoutChecker.start();
518         JSValue resultV = JSC::construct(exec, constructor, constructType, constructData, argList);
519         globalData->timeoutChecker.stop();
520 
521         // Convert and return the result.
522         convertValueToNPVariant(exec, resultV, result);
523         exec->clearException();
524         return true;
525     }
526 
527     if (NP_CLASS_STRUCT_VERSION_HAS_CTOR(o->_class) && o->_class->construct)
528         return o->_class->construct(o, args, argCount, result);
529 
530     return false;
531 }
532 
533 #endif // ENABLE(NETSCAPE_PLUGIN_API)
534