d9a113016326e5bdc9b55acdd2d2dc4845899da8
[WebKit-https.git] / Source / WebCore / bridge / NP_jsobject.cpp
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
49 using namespace JSC;
50 using namespace JSC::Bindings;
51 using namespace WebCore;
52
53 class ObjectMap {
54 public:
55     NPObject* get(RootObject* rootObject, JSObject* jsObject)
56     {
57         return m_map.get(rootObject).get(jsObject);
58     }
59
60     void add(RootObject* rootObject, JSObject* jsObject, NPObject* npObject)
61     {
62         HashMap<RootObject*, JSToNPObjectMap>::iterator iter = m_map.find(rootObject);
63         if (iter == m_map.end()) {
64             rootObject->addInvalidationCallback(&m_invalidationCallback);
65             iter = m_map.add(rootObject, JSToNPObjectMap()).iterator;
66         }
67
68         ASSERT(iter->second.find(jsObject) == iter->second.end());
69         iter->second.add(jsObject, npObject);
70     }
71
72     void remove(RootObject* rootObject)
73     {
74         HashMap<RootObject*, JSToNPObjectMap>::iterator iter = m_map.find(rootObject);
75         ASSERT(iter != m_map.end());
76         m_map.remove(iter);
77     }
78
79     void remove(RootObject* rootObject, JSObject* jsObject)
80     {
81         HashMap<RootObject*, JSToNPObjectMap>::iterator iter = m_map.find(rootObject);
82         ASSERT(iter != m_map.end());
83         ASSERT(iter->second.find(jsObject) != iter->second.end());
84
85         iter->second.remove(jsObject);
86     }
87
88 private:
89     struct RootObjectInvalidationCallback : public RootObject::InvalidationCallback {
90         virtual void operator()(RootObject*);
91     };
92     RootObjectInvalidationCallback m_invalidationCallback;
93
94     // JSObjects are protected by RootObject.
95     typedef HashMap<JSObject*, NPObject*> JSToNPObjectMap;
96     HashMap<RootObject*, JSToNPObjectMap> m_map;
97 };
98
99
100 static ObjectMap& objectMap()
101 {
102     DEFINE_STATIC_LOCAL(ObjectMap, map, ());
103     return map;
104 }
105
106 void ObjectMap::RootObjectInvalidationCallback::operator()(RootObject* rootObject)
107 {
108     objectMap().remove(rootObject);
109 }
110
111 static void getListFromVariantArgs(ExecState* exec, const NPVariant* args, unsigned argCount, RootObject* rootObject, MarkedArgumentBuffer& aList)
112 {
113     for (unsigned i = 0; i < argCount; ++i)
114         aList.append(convertNPVariantToValue(exec, &args[i], rootObject));
115 }
116
117 static NPObject* jsAllocate(NPP, NPClass*)
118 {
119     return static_cast<NPObject*>(malloc(sizeof(JavaScriptObject)));
120 }
121
122 static void jsDeallocate(NPObject* npObj)
123 {
124     JavaScriptObject* obj = reinterpret_cast<JavaScriptObject*>(npObj);
125
126     if (obj->rootObject && obj->rootObject->isValid()) {
127         objectMap().remove(obj->rootObject, obj->imp);
128         obj->rootObject->gcUnprotect(obj->imp);
129     }
130
131     if (obj->rootObject)
132         obj->rootObject->deref();
133
134     free(obj);
135 }
136
137 static NPClass javascriptClass = { 1, jsAllocate, jsDeallocate, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 };
138 static NPClass noScriptClass = { 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 };
139
140 NPClass* NPScriptObjectClass = &javascriptClass;
141 static NPClass* NPNoScriptObjectClass = &noScriptClass;
142
143 NPObject* _NPN_CreateScriptObject(NPP npp, JSObject* imp, PassRefPtr<RootObject> rootObject)
144 {
145     if (NPObject* object = objectMap().get(rootObject.get(), imp))
146         return _NPN_RetainObject(object);
147
148     JavaScriptObject* obj = reinterpret_cast<JavaScriptObject*>(_NPN_CreateObject(npp, NPScriptObjectClass));
149
150     obj->rootObject = rootObject.leakRef();
151
152     if (obj->rootObject) {
153         obj->rootObject->gcProtect(imp);
154         objectMap().add(obj->rootObject, imp, reinterpret_cast<NPObject*>(obj));
155     }
156
157     obj->imp = imp;
158
159     return reinterpret_cast<NPObject*>(obj);
160 }
161
162 NPObject* _NPN_CreateNoScriptObject(void)
163 {
164     return _NPN_CreateObject(0, NPNoScriptObjectClass);
165 }
166
167 bool _NPN_InvokeDefault(NPP, NPObject* o, const NPVariant* args, uint32_t argCount, NPVariant* result)
168 {
169     if (o->_class == NPScriptObjectClass) {
170         JavaScriptObject* obj = reinterpret_cast<JavaScriptObject*>(o); 
171         
172         VOID_TO_NPVARIANT(*result);
173         
174         // Lookup the function object.
175         RootObject* rootObject = obj->rootObject;
176         if (!rootObject || !rootObject->isValid())
177             return false;
178         
179         ExecState* exec = rootObject->globalObject()->globalExec();
180         JSLockHolder lock(exec);
181         
182         // Call the function object.
183         JSValue function = obj->imp;
184         CallData callData;
185         CallType callType = getCallData(function, callData);
186         if (callType == CallTypeNone)
187             return false;
188         
189         MarkedArgumentBuffer argList;
190         getListFromVariantArgs(exec, args, argCount, rootObject, argList);
191         JSValue resultV = JSC::call(exec, function, callType, callData, function, argList);
192
193         // Convert and return the result of the function call.
194         convertValueToNPVariant(exec, resultV, result);
195         exec->clearException();
196         return true;        
197     }
198
199     if (o->_class->invokeDefault)
200         return o->_class->invokeDefault(o, args, argCount, result);    
201     VOID_TO_NPVARIANT(*result);
202     return true;
203 }
204
205 bool _NPN_Invoke(NPP npp, NPObject* o, NPIdentifier methodName, const NPVariant* args, uint32_t argCount, NPVariant* result)
206 {
207     if (o->_class == NPScriptObjectClass) {
208         JavaScriptObject* obj = reinterpret_cast<JavaScriptObject*>(o); 
209
210         IdentifierRep* i = static_cast<IdentifierRep*>(methodName);
211         if (!i->isString())
212             return false;
213
214         // Special case the "eval" method.
215         if (methodName == _NPN_GetStringIdentifier("eval")) {
216             if (argCount != 1)
217                 return false;
218             if (args[0].type != NPVariantType_String)
219                 return false;
220             return _NPN_Evaluate(npp, o, const_cast<NPString*>(&args[0].value.stringValue), result);
221         }
222
223         // Look up the function object.
224         RootObject* rootObject = obj->rootObject;
225         if (!rootObject || !rootObject->isValid())
226             return false;
227         ExecState* exec = rootObject->globalObject()->globalExec();
228         JSLockHolder lock(exec);
229         JSValue function = obj->imp->get(exec, identifierFromNPIdentifier(exec, i->string()));
230         CallData callData;
231         CallType callType = getCallData(function, callData);
232         if (callType == CallTypeNone)
233             return false;
234
235         // Call the function object.
236         MarkedArgumentBuffer argList;
237         getListFromVariantArgs(exec, args, argCount, rootObject, argList);
238         JSValue resultV = JSC::call(exec, function, callType, callData, obj->imp->methodTable()->toThisObject(obj->imp, exec), argList);
239
240         // Convert and return the result of the function call.
241         convertValueToNPVariant(exec, resultV, result);
242         exec->clearException();
243         return true;
244     }
245
246     if (o->_class->invoke)
247         return o->_class->invoke(o, methodName, args, argCount, result);
248     
249     VOID_TO_NPVARIANT(*result);
250     return true;
251 }
252
253 bool _NPN_Evaluate(NPP instance, NPObject* o, NPString* s, NPVariant* variant)
254 {
255     if (o->_class == NPScriptObjectClass) {
256         JavaScriptObject* obj = reinterpret_cast<JavaScriptObject*>(o); 
257
258         RootObject* rootObject = obj->rootObject;
259         if (!rootObject || !rootObject->isValid())
260             return false;
261
262         // There is a crash in Flash when evaluating a script that destroys the
263         // PluginView, so we destroy it asynchronously.
264         PluginView::keepAlive(instance);
265
266         ExecState* exec = rootObject->globalObject()->globalExec();
267         JSLockHolder lock(exec);
268         String scriptString = convertNPStringToUTF16(s);
269         
270         JSValue returnValue = JSC::evaluate(rootObject->globalObject()->globalExec(), makeSource(scriptString), JSC::JSValue());
271
272         convertValueToNPVariant(exec, returnValue, variant);
273         exec->clearException();
274         return true;
275     }
276
277     VOID_TO_NPVARIANT(*variant);
278     return false;
279 }
280
281 bool _NPN_GetProperty(NPP, NPObject* o, NPIdentifier propertyName, NPVariant* variant)
282 {
283     if (o->_class == NPScriptObjectClass) {
284         JavaScriptObject* obj = reinterpret_cast<JavaScriptObject*>(o); 
285
286         RootObject* rootObject = obj->rootObject;
287         if (!rootObject || !rootObject->isValid())
288             return false;
289
290         ExecState* exec = rootObject->globalObject()->globalExec();
291         IdentifierRep* i = static_cast<IdentifierRep*>(propertyName);
292         
293         JSLockHolder lock(exec);
294         JSValue result;
295         if (i->isString())
296             result = obj->imp->get(exec, identifierFromNPIdentifier(exec, i->string()));
297         else
298             result = obj->imp->get(exec, i->number());
299
300         convertValueToNPVariant(exec, result, variant);
301         exec->clearException();
302         return true;
303     }
304
305     if (o->_class->hasProperty && o->_class->getProperty) {
306         if (o->_class->hasProperty(o, propertyName))
307             return o->_class->getProperty(o, propertyName, variant);
308         return false;
309     }
310
311     VOID_TO_NPVARIANT(*variant);
312     return false;
313 }
314
315 bool _NPN_SetProperty(NPP, NPObject* o, NPIdentifier propertyName, const NPVariant* variant)
316 {
317     if (o->_class == NPScriptObjectClass) {
318         JavaScriptObject* obj = reinterpret_cast<JavaScriptObject*>(o); 
319
320         RootObject* rootObject = obj->rootObject;
321         if (!rootObject || !rootObject->isValid())
322             return false;
323
324         ExecState* exec = rootObject->globalObject()->globalExec();
325         JSLockHolder lock(exec);
326         IdentifierRep* i = static_cast<IdentifierRep*>(propertyName);
327
328         if (i->isString()) {
329             PutPropertySlot slot;
330             obj->imp->methodTable()->put(obj->imp, exec, identifierFromNPIdentifier(exec, i->string()), convertNPVariantToValue(exec, variant, rootObject), slot);
331         } else
332             obj->imp->methodTable()->putByIndex(obj->imp, exec, i->number(), convertNPVariantToValue(exec, variant, rootObject), false);
333         exec->clearException();
334         return true;
335     }
336
337     if (o->_class->setProperty)
338         return o->_class->setProperty(o, propertyName, variant);
339
340     return false;
341 }
342
343 bool _NPN_RemoveProperty(NPP, NPObject* o, NPIdentifier propertyName)
344 {
345     if (o->_class == NPScriptObjectClass) {
346         JavaScriptObject* obj = reinterpret_cast<JavaScriptObject*>(o); 
347
348         RootObject* rootObject = obj->rootObject;
349         if (!rootObject || !rootObject->isValid())
350             return false;
351
352         ExecState* exec = rootObject->globalObject()->globalExec();
353         IdentifierRep* i = static_cast<IdentifierRep*>(propertyName);
354         if (i->isString()) {
355             if (!obj->imp->hasProperty(exec, identifierFromNPIdentifier(exec, i->string()))) {
356                 exec->clearException();
357                 return false;
358             }
359         } else {
360             if (!obj->imp->hasProperty(exec, i->number())) {
361                 exec->clearException();
362                 return false;
363             }
364         }
365
366         JSLockHolder lock(exec);
367         if (i->isString())
368             obj->imp->methodTable()->deleteProperty(obj->imp, exec, identifierFromNPIdentifier(exec, i->string()));
369         else
370             obj->imp->methodTable()->deletePropertyByIndex(obj->imp, exec, i->number());
371
372         exec->clearException();
373         return true;
374     }
375     return false;
376 }
377
378 bool _NPN_HasProperty(NPP, NPObject* o, NPIdentifier propertyName)
379 {
380     if (o->_class == NPScriptObjectClass) {
381         JavaScriptObject* obj = reinterpret_cast<JavaScriptObject*>(o); 
382
383         RootObject* rootObject = obj->rootObject;
384         if (!rootObject || !rootObject->isValid())
385             return false;
386
387         ExecState* exec = rootObject->globalObject()->globalExec();
388         IdentifierRep* i = static_cast<IdentifierRep*>(propertyName);
389         JSLockHolder lock(exec);
390         if (i->isString()) {
391             bool result = obj->imp->hasProperty(exec, identifierFromNPIdentifier(exec, i->string()));
392             exec->clearException();
393             return result;
394         }
395
396         bool result = obj->imp->hasProperty(exec, i->number());
397         exec->clearException();
398         return result;
399     }
400
401     if (o->_class->hasProperty)
402         return o->_class->hasProperty(o, propertyName);
403
404     return false;
405 }
406
407 bool _NPN_HasMethod(NPP, NPObject* o, NPIdentifier methodName)
408 {
409     if (o->_class == NPScriptObjectClass) {
410         JavaScriptObject* obj = reinterpret_cast<JavaScriptObject*>(o); 
411
412         IdentifierRep* i = static_cast<IdentifierRep*>(methodName);
413         if (!i->isString())
414             return false;
415
416         RootObject* rootObject = obj->rootObject;
417         if (!rootObject || !rootObject->isValid())
418             return false;
419
420         ExecState* exec = rootObject->globalObject()->globalExec();
421         JSLockHolder lock(exec);
422         JSValue func = obj->imp->get(exec, identifierFromNPIdentifier(exec, i->string()));
423         exec->clearException();
424         return !func.isUndefined();
425     }
426     
427     if (o->_class->hasMethod)
428         return o->_class->hasMethod(o, methodName);
429     
430     return false;
431 }
432
433 void _NPN_SetException(NPObject*, const NPUTF8* message)
434 {
435     // Ignoring the NPObject param is consistent with the Mozilla implementation.
436     String exception(message);
437     CInstance::setGlobalException(exception);
438 }
439
440 bool _NPN_Enumerate(NPP, NPObject* o, NPIdentifier** identifier, uint32_t* count)
441 {
442     if (o->_class == NPScriptObjectClass) {
443         JavaScriptObject* obj = reinterpret_cast<JavaScriptObject*>(o); 
444         
445         RootObject* rootObject = obj->rootObject;
446         if (!rootObject || !rootObject->isValid())
447             return false;
448         
449         ExecState* exec = rootObject->globalObject()->globalExec();
450         JSLockHolder lock(exec);
451         PropertyNameArray propertyNames(exec);
452
453         obj->imp->methodTable()->getPropertyNames(obj->imp, exec, propertyNames, ExcludeDontEnumProperties);
454         unsigned size = static_cast<unsigned>(propertyNames.size());
455         // FIXME: This should really call NPN_MemAlloc but that's in WebKit
456         NPIdentifier* identifiers = static_cast<NPIdentifier*>(malloc(sizeof(NPIdentifier) * size));
457         
458         for (unsigned i = 0; i < size; ++i)
459             identifiers[i] = _NPN_GetStringIdentifier(propertyNames[i].ustring().utf8().data());
460
461         *identifier = identifiers;
462         *count = size;
463
464         exec->clearException();
465         return true;
466     }
467     
468     if (NP_CLASS_STRUCT_VERSION_HAS_ENUM(o->_class) && o->_class->enumerate)
469         return o->_class->enumerate(o, identifier, count);
470     
471     return false;
472 }
473
474 bool _NPN_Construct(NPP, NPObject* o, const NPVariant* args, uint32_t argCount, NPVariant* result)
475 {
476     if (o->_class == NPScriptObjectClass) {
477         JavaScriptObject* obj = reinterpret_cast<JavaScriptObject*>(o);
478         
479         VOID_TO_NPVARIANT(*result);
480         
481         // Lookup the constructor object.
482         RootObject* rootObject = obj->rootObject;
483         if (!rootObject || !rootObject->isValid())
484             return false;
485         
486         ExecState* exec = rootObject->globalObject()->globalExec();
487         JSLockHolder lock(exec);
488         
489         // Call the constructor object.
490         JSValue constructor = obj->imp;
491         ConstructData constructData;
492         ConstructType constructType = getConstructData(constructor, constructData);
493         if (constructType == ConstructTypeNone)
494             return false;
495         
496         MarkedArgumentBuffer argList;
497         getListFromVariantArgs(exec, args, argCount, rootObject, argList);
498         JSValue resultV = JSC::construct(exec, constructor, constructType, constructData, argList);
499         
500         // Convert and return the result.
501         convertValueToNPVariant(exec, resultV, result);
502         exec->clearException();
503         return true;
504     }
505     
506     if (NP_CLASS_STRUCT_VERSION_HAS_CTOR(o->_class) && o->_class->construct)
507         return o->_class->construct(o, args, argCount, result);
508     
509     return false;
510 }
511
512 #endif // ENABLE(NETSCAPE_PLUGIN_API)