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
50using namespace JSC;
51using namespace JSC::Bindings;
52using namespace WebCore;
53
54class ObjectMap {
55public:
56    NPObject* get(RootObject* rootObject, JSObject* jsObject)
57    {
58        return m_map.get(rootObject).get(jsObject);
59    }
60
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
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
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
89private:
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
101static ObjectMap& objectMap()
102{
103    DEFINE_STATIC_LOCAL(ObjectMap, map, ());
104    return map;
105}
106
107void ObjectMap::RootObjectInvalidationCallback::operator()(RootObject* rootObject)
108{
109    objectMap().remove(rootObject);
110}
111
112static 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
118static NPObject* jsAllocate(NPP, NPClass*)
119{
120    return static_cast<NPObject*>(malloc(sizeof(JavaScriptObject)));
121}
122
123static 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
138static NPClass javascriptClass = { 1, jsAllocate, jsDeallocate, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 };
139static NPClass noScriptClass = { 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 };
140
141NPClass* NPScriptObjectClass = &javascriptClass;
142static NPClass* NPNoScriptObjectClass = &noScriptClass;
143
144NPObject* _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
163NPObject* _NPN_CreateNoScriptObject(void)
164{
165    return _NPN_CreateObject(0, NPNoScriptObjectClass);
166}
167
168bool _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
209bool _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
260bool _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
299bool _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
333bool _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
361bool _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
396bool _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
425bool _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
451void _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
458bool _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
492bool _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