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