1/*
2 * Copyright (C) 2003, 2006 Apple 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 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 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
57static String& globalExceptionString()
58{
59    DEPRECATED_DEFINE_STATIC_LOCAL(String, exceptionStr, ());
60    return exceptionStr;
61}
62
63void CInstance::setGlobalException(String exception)
64{
65    globalExceptionString() = exception;
66}
67
68void CInstance::moveGlobalExceptionToExecState(ExecState* exec)
69{
70    if (globalExceptionString().isNull())
71        return;
72
73    {
74        JSLockHolder lock(exec);
75        exec->vm().throwException(exec, createError(exec, globalExceptionString()));
76    }
77
78    globalExceptionString() = String();
79}
80
81CInstance::CInstance(NPObject* o, PassRefPtr<RootObject> rootObject)
82    : Instance(rootObject)
83{
84    _object = _NPN_RetainObject(o);
85    _class = 0;
86}
87
88CInstance::~CInstance()
89{
90    _NPN_ReleaseObject(_object);
91}
92
93RuntimeObject* CInstance::newRuntimeObject(ExecState* exec)
94{
95    // FIXME: deprecatedGetDOMStructure uses the prototype off of the wrong global object.
96    return CRuntimeObject::create(exec->vm(), WebCore::deprecatedGetDOMStructure<CRuntimeObject>(exec), this);
97}
98
99Class *CInstance::getClass() const
100{
101    if (!_class)
102        _class = CClass::classForIsA(_object->_class);
103    return _class;
104}
105
106bool CInstance::supportsInvokeDefaultMethod() const
107{
108    return _object->_class->invokeDefault;
109}
110
111class CRuntimeMethod : public RuntimeMethod {
112public:
113    typedef RuntimeMethod Base;
114
115    static CRuntimeMethod* create(ExecState* exec, JSGlobalObject* globalObject, const String& name, Bindings::Method* method)
116    {
117        // FIXME: deprecatedGetDOMStructure uses the prototype off of the wrong global object
118        // We need to pass in the right global object for "i".
119        Structure* domStructure = WebCore::deprecatedGetDOMStructure<CRuntimeMethod>(exec);
120        CRuntimeMethod* runtimeMethod = new (NotNull, allocateCell<CRuntimeMethod>(*exec->heap())) CRuntimeMethod(globalObject, domStructure, method);
121        runtimeMethod->finishCreation(exec->vm(), name);
122        return runtimeMethod;
123    }
124
125    static Structure* createStructure(VM& vm, JSGlobalObject* globalObject, JSValue prototype)
126    {
127        return Structure::create(vm, globalObject, prototype, TypeInfo(ObjectType, StructureFlags), info());
128    }
129
130    DECLARE_INFO;
131
132private:
133    CRuntimeMethod(JSGlobalObject* globalObject, Structure* structure, Bindings::Method* method)
134        : RuntimeMethod(globalObject, structure, method)
135    {
136    }
137
138    void finishCreation(VM& vm, const String& name)
139    {
140        Base::finishCreation(vm, name);
141        ASSERT(inherits(info()));
142    }
143
144};
145
146const ClassInfo CRuntimeMethod::s_info = { "CRuntimeMethod", &RuntimeMethod::s_info, 0, 0, CREATE_METHOD_TABLE(CRuntimeMethod) };
147
148JSValue CInstance::getMethod(ExecState* exec, PropertyName propertyName)
149{
150    Method* method = getClass()->methodNamed(propertyName, this);
151    return CRuntimeMethod::create(exec, exec->lexicalGlobalObject(), propertyName.publicName(), method);
152}
153
154JSValue CInstance::invokeMethod(ExecState* exec, RuntimeMethod* runtimeMethod)
155{
156    if (!asObject(runtimeMethod)->inherits(CRuntimeMethod::info()))
157        return exec->vm().throwException(exec, createTypeError(exec, "Attempt to invoke non-plug-in method on plug-in object."));
158
159    CMethod* method = static_cast<CMethod*>(runtimeMethod->method());
160    ASSERT(method);
161
162    NPIdentifier ident = method->identifier();
163    if (!_object->_class->hasMethod(_object, ident))
164        return jsUndefined();
165
166    unsigned count = exec->argumentCount();
167    Vector<NPVariant, 8> cArgs(count);
168
169    unsigned i;
170    for (i = 0; i < count; i++)
171        convertValueToNPVariant(exec, exec->uncheckedArgument(i), &cArgs[i]);
172
173    // Invoke the 'C' method.
174    bool retval = true;
175    NPVariant resultVariant;
176    VOID_TO_NPVARIANT(resultVariant);
177
178    {
179        JSLock::DropAllLocks dropAllLocks(exec);
180        ASSERT(globalExceptionString().isNull());
181        retval = _object->_class->invoke(_object, ident, cArgs.data(), count, &resultVariant);
182        moveGlobalExceptionToExecState(exec);
183    }
184
185    if (!retval)
186        exec->vm().throwException(exec, createError(exec, ASCIILiteral("Error calling method on NPObject.")));
187
188    for (i = 0; i < count; i++)
189        _NPN_ReleaseVariantValue(&cArgs[i]);
190
191    JSValue resultValue = convertNPVariantToValue(exec, &resultVariant, m_rootObject.get());
192    _NPN_ReleaseVariantValue(&resultVariant);
193    return resultValue;
194}
195
196
197JSValue CInstance::invokeDefaultMethod(ExecState* exec)
198{
199    if (!_object->_class->invokeDefault)
200        return jsUndefined();
201
202    unsigned count = exec->argumentCount();
203    Vector<NPVariant, 8> cArgs(count);
204
205    unsigned i;
206    for (i = 0; i < count; i++)
207        convertValueToNPVariant(exec, exec->uncheckedArgument(i), &cArgs[i]);
208
209    // Invoke the 'C' method.
210    bool retval = true;
211    NPVariant resultVariant;
212    VOID_TO_NPVARIANT(resultVariant);
213    {
214        JSLock::DropAllLocks dropAllLocks(exec);
215        ASSERT(globalExceptionString().isNull());
216        retval = _object->_class->invokeDefault(_object, cArgs.data(), count, &resultVariant);
217        moveGlobalExceptionToExecState(exec);
218    }
219
220    if (!retval)
221        exec->vm().throwException(exec, createError(exec, ASCIILiteral("Error calling method on NPObject.")));
222
223    for (i = 0; i < count; i++)
224        _NPN_ReleaseVariantValue(&cArgs[i]);
225
226    JSValue resultValue = convertNPVariantToValue(exec, &resultVariant, m_rootObject.get());
227    _NPN_ReleaseVariantValue(&resultVariant);
228    return resultValue;
229}
230
231bool CInstance::supportsConstruct() const
232{
233    return _object->_class->construct;
234}
235
236JSValue CInstance::invokeConstruct(ExecState* exec, const ArgList& args)
237{
238    if (!_object->_class->construct)
239        return jsUndefined();
240
241    unsigned count = args.size();
242    Vector<NPVariant, 8> cArgs(count);
243
244    unsigned i;
245    for (i = 0; i < count; i++)
246        convertValueToNPVariant(exec, args.at(i), &cArgs[i]);
247
248    // Invoke the 'C' method.
249    bool retval = true;
250    NPVariant resultVariant;
251    VOID_TO_NPVARIANT(resultVariant);
252    {
253        JSLock::DropAllLocks dropAllLocks(exec);
254        ASSERT(globalExceptionString().isNull());
255        retval = _object->_class->construct(_object, cArgs.data(), count, &resultVariant);
256        moveGlobalExceptionToExecState(exec);
257    }
258
259    if (!retval)
260        exec->vm().throwException(exec, createError(exec, ASCIILiteral("Error calling method on NPObject.")));
261
262    for (i = 0; i < count; i++)
263        _NPN_ReleaseVariantValue(&cArgs[i]);
264
265    JSValue resultValue = convertNPVariantToValue(exec, &resultVariant, m_rootObject.get());
266    _NPN_ReleaseVariantValue(&resultVariant);
267    return resultValue;
268}
269
270JSValue CInstance::defaultValue(ExecState* exec, PreferredPrimitiveType hint) const
271{
272    if (hint == PreferString)
273        return stringValue(exec);
274    if (hint == PreferNumber)
275        return numberValue(exec);
276    return valueOf(exec);
277}
278
279JSValue CInstance::stringValue(ExecState* exec) const
280{
281    JSValue value;
282    if (toJSPrimitive(exec, "toString", value))
283        return value;
284
285    // Fallback to default implementation.
286    return jsNontrivialString(exec, ASCIILiteral("NPObject"));
287}
288
289JSValue CInstance::numberValue(ExecState*) const
290{
291    // FIXME: Implement something sensible.
292    return jsNumber(0);
293}
294
295JSValue CInstance::booleanValue() const
296{
297    // As per ECMA 9.2.
298    return jsBoolean(getObject());
299}
300
301JSValue CInstance::valueOf(ExecState* exec) const
302{
303    JSValue value;
304    if (toJSPrimitive(exec, "valueOf", value))
305        return value;
306
307    // Fallback to default implementation.
308    return stringValue(exec);
309}
310
311bool CInstance::toJSPrimitive(ExecState* exec, const char* name, JSValue& resultValue) const
312{
313    NPIdentifier ident = _NPN_GetStringIdentifier(name);
314    if (!_object->_class->hasMethod(_object, ident))
315        return false;
316
317    // Invoke the 'C' method.
318    bool retval = true;
319    NPVariant resultVariant;
320    VOID_TO_NPVARIANT(resultVariant);
321
322    {
323        JSLock::DropAllLocks dropAllLocks(exec);
324        ASSERT(globalExceptionString().isNull());
325        retval = _object->_class->invoke(_object, ident, 0, 0, &resultVariant);
326        moveGlobalExceptionToExecState(exec);
327    }
328
329    if (!retval)
330        exec->vm().throwException(exec, createError(exec, ASCIILiteral("Error calling method on NPObject.")));
331
332    resultValue = convertNPVariantToValue(exec, &resultVariant, m_rootObject.get());
333    _NPN_ReleaseVariantValue(&resultVariant);
334    return true;
335}
336
337void CInstance::getPropertyNames(ExecState* exec, PropertyNameArray& nameArray)
338{
339    if (!NP_CLASS_STRUCT_VERSION_HAS_ENUM(_object->_class) || !_object->_class->enumerate)
340        return;
341
342    uint32_t count;
343    NPIdentifier* identifiers;
344
345    {
346        JSLock::DropAllLocks dropAllLocks(exec);
347        ASSERT(globalExceptionString().isNull());
348        bool ok = _object->_class->enumerate(_object, &identifiers, &count);
349        moveGlobalExceptionToExecState(exec);
350        if (!ok)
351            return;
352    }
353
354    for (uint32_t i = 0; i < count; i++) {
355        IdentifierRep* identifier = static_cast<IdentifierRep*>(identifiers[i]);
356
357        if (identifier->isString())
358            nameArray.add(identifierFromNPIdentifier(exec, identifier->string()));
359        else
360            nameArray.add(Identifier::from(exec, identifier->number()));
361    }
362
363    // FIXME: This should really call NPN_MemFree but that's in WebKit
364    free(identifiers);
365}
366
367}
368}
369
370#endif // ENABLE(NETSCAPE_PLUGIN_API)
371