1/*
2 * Copyright (C) 2003, 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 "c_instance.h"
31
32#include "CRuntimeObject.h"
33#include "IdentifierRep.h"
34#include "JSDOMBinding.h"
35#include "c_class.h"
36#include "c_runtime.h"
37#include "c_utility.h"
38#include "npruntime_impl.h"
39#include "runtime_method.h"
40#include "runtime_root.h"
41#include <interpreter/CallFrame.h>
42#include <runtime/ArgList.h>
43#include <runtime/Error.h>
44#include <runtime/FunctionPrototype.h>
45#include <runtime/JSLock.h>
46#include <runtime/PropertyNameArray.h>
47#include <wtf/Assertions.h>
48#include <wtf/StdLibExtras.h>
49#include <wtf/StringExtras.h>
50#include <wtf/Vector.h>
51
52using namespace WebCore;
53
54namespace JSC {
55namespace Bindings {
56
57using JSC::UString;
58
59static JSC::UString& globalExceptionString()
60{
61    DEFINE_STATIC_LOCAL(JSC::UString, exceptionStr, ());
62    return exceptionStr;
63}
64
65void CInstance::setGlobalException(UString exception)
66{
67    globalExceptionString() = exception;
68}
69
70void CInstance::moveGlobalExceptionToExecState(ExecState* exec)
71{
72    if (globalExceptionString().isNull())
73        return;
74
75    {
76        JSLock lock(SilenceAssertionsOnly);
77        throwError(exec, createError(exec, globalExceptionString()));
78    }
79
80    globalExceptionString() = UString();
81}
82
83CInstance::CInstance(NPObject* o, PassRefPtr<RootObject> rootObject)
84    : Instance(rootObject)
85{
86    _object = _NPN_RetainObject(o);
87    _class = 0;
88}
89
90CInstance::~CInstance()
91{
92    _NPN_ReleaseObject(_object);
93}
94
95RuntimeObject* CInstance::newRuntimeObject(ExecState* exec)
96{
97    return new (exec) CRuntimeObject(exec, exec->lexicalGlobalObject(), this);
98}
99
100Class *CInstance::getClass() const
101{
102    if (!_class)
103        _class = CClass::classForIsA(_object->_class);
104    return _class;
105}
106
107bool CInstance::supportsInvokeDefaultMethod() const
108{
109    return _object->_class->invokeDefault;
110}
111
112class CRuntimeMethod : public RuntimeMethod {
113public:
114    CRuntimeMethod(ExecState* exec, JSGlobalObject* globalObject, const Identifier& name, Bindings::MethodList& list)
115        // FIXME: deprecatedGetDOMStructure uses the prototype off of the wrong global object
116        // We need to pass in the right global object for "i".
117        : RuntimeMethod(exec, globalObject, WebCore::deprecatedGetDOMStructure<CRuntimeMethod>(exec), name, list)
118    {
119        ASSERT(inherits(&s_info));
120    }
121
122    static Structure* createStructure(JSGlobalData& globalData, JSValue prototype)
123    {
124        return Structure::create(globalData, prototype, TypeInfo(ObjectType, StructureFlags), AnonymousSlotCount, &s_info);
125    }
126
127    static const ClassInfo s_info;
128};
129
130const ClassInfo CRuntimeMethod::s_info = { "CRuntimeMethod", &RuntimeMethod::s_info, 0, 0 };
131
132JSValue CInstance::getMethod(ExecState* exec, const Identifier& propertyName)
133{
134    MethodList methodList = getClass()->methodsNamed(propertyName, this);
135    return new (exec) CRuntimeMethod(exec, exec->lexicalGlobalObject(), propertyName, methodList);
136}
137
138JSValue CInstance::invokeMethod(ExecState* exec, RuntimeMethod* runtimeMethod)
139{
140    if (!asObject(runtimeMethod)->inherits(&CRuntimeMethod::s_info))
141        return throwError(exec, createTypeError(exec, "Attempt to invoke non-plug-in method on plug-in object."));
142
143    const MethodList& methodList = *runtimeMethod->methods();
144
145    // Overloading methods are not allowed by NPObjects.  Should only be one
146    // name match for a particular method.
147    ASSERT(methodList.size() == 1);
148
149    CMethod* method = static_cast<CMethod*>(methodList[0]);
150
151    NPIdentifier ident = method->identifier();
152    if (!_object->_class->hasMethod(_object, ident))
153        return jsUndefined();
154
155    unsigned count = exec->argumentCount();
156    Vector<NPVariant, 8> cArgs(count);
157
158    unsigned i;
159    for (i = 0; i < count; i++)
160        convertValueToNPVariant(exec, exec->argument(i), &cArgs[i]);
161
162    // Invoke the 'C' method.
163    bool retval = true;
164    NPVariant resultVariant;
165    VOID_TO_NPVARIANT(resultVariant);
166
167    {
168        JSLock::DropAllLocks dropAllLocks(SilenceAssertionsOnly);
169        ASSERT(globalExceptionString().isNull());
170        retval = _object->_class->invoke(_object, ident, cArgs.data(), count, &resultVariant);
171        moveGlobalExceptionToExecState(exec);
172    }
173
174    if (!retval)
175        throwError(exec, createError(exec, "Error calling method on NPObject."));
176
177    for (i = 0; i < count; i++)
178        _NPN_ReleaseVariantValue(&cArgs[i]);
179
180    JSValue resultValue = convertNPVariantToValue(exec, &resultVariant, m_rootObject.get());
181    _NPN_ReleaseVariantValue(&resultVariant);
182    return resultValue;
183}
184
185
186JSValue CInstance::invokeDefaultMethod(ExecState* exec)
187{
188    if (!_object->_class->invokeDefault)
189        return jsUndefined();
190
191    unsigned count = exec->argumentCount();
192    Vector<NPVariant, 8> cArgs(count);
193
194    unsigned i;
195    for (i = 0; i < count; i++)
196        convertValueToNPVariant(exec, exec->argument(i), &cArgs[i]);
197
198    // Invoke the 'C' method.
199    bool retval = true;
200    NPVariant resultVariant;
201    VOID_TO_NPVARIANT(resultVariant);
202    {
203        JSLock::DropAllLocks dropAllLocks(SilenceAssertionsOnly);
204        ASSERT(globalExceptionString().isNull());
205        retval = _object->_class->invokeDefault(_object, cArgs.data(), count, &resultVariant);
206        moveGlobalExceptionToExecState(exec);
207    }
208
209    if (!retval)
210        throwError(exec, createError(exec, "Error calling method on NPObject."));
211
212    for (i = 0; i < count; i++)
213        _NPN_ReleaseVariantValue(&cArgs[i]);
214
215    JSValue resultValue = convertNPVariantToValue(exec, &resultVariant, m_rootObject.get());
216    _NPN_ReleaseVariantValue(&resultVariant);
217    return resultValue;
218}
219
220bool CInstance::supportsConstruct() const
221{
222    return _object->_class->construct;
223}
224
225JSValue CInstance::invokeConstruct(ExecState* exec, const ArgList& args)
226{
227    if (!_object->_class->construct)
228        return jsUndefined();
229
230    unsigned count = args.size();
231    Vector<NPVariant, 8> cArgs(count);
232
233    unsigned i;
234    for (i = 0; i < count; i++)
235        convertValueToNPVariant(exec, args.at(i), &cArgs[i]);
236
237    // Invoke the 'C' method.
238    bool retval = true;
239    NPVariant resultVariant;
240    VOID_TO_NPVARIANT(resultVariant);
241    {
242        JSLock::DropAllLocks dropAllLocks(SilenceAssertionsOnly);
243        ASSERT(globalExceptionString().isNull());
244        retval = _object->_class->construct(_object, cArgs.data(), count, &resultVariant);
245        moveGlobalExceptionToExecState(exec);
246    }
247
248    if (!retval)
249        throwError(exec, createError(exec, "Error calling method on NPObject."));
250
251    for (i = 0; i < count; i++)
252        _NPN_ReleaseVariantValue(&cArgs[i]);
253
254    JSValue resultValue = convertNPVariantToValue(exec, &resultVariant, m_rootObject.get());
255    _NPN_ReleaseVariantValue(&resultVariant);
256    return resultValue;
257}
258
259JSValue CInstance::defaultValue(ExecState* exec, PreferredPrimitiveType hint) const
260{
261    if (hint == PreferString)
262        return stringValue(exec);
263    if (hint == PreferNumber)
264        return numberValue(exec);
265    return valueOf(exec);
266}
267
268JSValue CInstance::stringValue(ExecState* exec) const
269{
270    char buf[1024];
271    snprintf(buf, sizeof(buf), "NPObject %p, NPClass %p", _object, _object->_class);
272    return jsString(exec, buf);
273}
274
275JSValue CInstance::numberValue(ExecState*) const
276{
277    // FIXME: Implement something sensible.
278    return jsNumber(0);
279}
280
281JSValue CInstance::booleanValue() const
282{
283    // FIXME: Implement something sensible.
284    return jsBoolean(false);
285}
286
287JSValue CInstance::valueOf(ExecState* exec) const
288{
289    return stringValue(exec);
290}
291
292void CInstance::getPropertyNames(ExecState* exec, PropertyNameArray& nameArray)
293{
294    if (!NP_CLASS_STRUCT_VERSION_HAS_ENUM(_object->_class) || !_object->_class->enumerate)
295        return;
296
297    uint32_t count;
298    NPIdentifier* identifiers;
299
300    {
301        JSLock::DropAllLocks dropAllLocks(SilenceAssertionsOnly);
302        ASSERT(globalExceptionString().isNull());
303        bool ok = _object->_class->enumerate(_object, &identifiers, &count);
304        moveGlobalExceptionToExecState(exec);
305        if (!ok)
306            return;
307    }
308
309    for (uint32_t i = 0; i < count; i++) {
310        IdentifierRep* identifier = static_cast<IdentifierRep*>(identifiers[i]);
311
312        if (identifier->isString())
313            nameArray.add(identifierFromNPIdentifier(exec, identifier->string()));
314        else
315            nameArray.add(Identifier::from(exec, identifier->number()));
316    }
317
318    // FIXME: This should really call NPN_MemFree but that's in WebKit
319    free(identifiers);
320}
321
322}
323}
324
325#endif // ENABLE(NETSCAPE_PLUGIN_API)
326