1/*
2 * Copyright (C) 2010 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. AND ITS CONTRIBUTORS ``AS IS''
14 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
15 * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
16 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
17 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
18 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
19 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
20 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
21 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
22 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
23 * THE POSSIBILITY OF SUCH DAMAGE.
24 */
25
26#include "config.h"
27#include "NPJSObject.h"
28
29#if ENABLE(NETSCAPE_PLUGIN_API)
30
31#include "JSNPObject.h"
32#include "NPRuntimeObjectMap.h"
33#include "NPRuntimeUtilities.h"
34#include <JavaScriptCore/JSCInlines.h>
35#include <JavaScriptCore/JSCellInlines.h>
36#include <JavaScriptCore/JSLock.h>
37#include <JavaScriptCore/JSObject.h>
38#include <JavaScriptCore/StrongInlines.h>
39#include <WebCore/Frame.h>
40#include <WebCore/IdentifierRep.h>
41#include <wtf/text/WTFString.h>
42
43using namespace JSC;
44using namespace WebCore;
45
46namespace WebKit {
47
48NPJSObject* NPJSObject::create(VM& vm, NPRuntimeObjectMap* objectMap, JSObject* jsObject)
49{
50    // We should never have a JSNPObject inside an NPJSObject.
51    ASSERT(!jsObject->inherits(JSNPObject::info()));
52
53    NPJSObject* npJSObject = toNPJSObject(createNPObject(0, npClass()));
54    npJSObject->initialize(vm, objectMap, jsObject);
55
56    return npJSObject;
57}
58
59NPJSObject::NPJSObject()
60    : m_objectMap(0)
61{
62}
63
64NPJSObject::~NPJSObject()
65{
66    m_objectMap->npJSObjectDestroyed(this);
67}
68
69bool NPJSObject::isNPJSObject(NPObject* npObject)
70{
71    return npObject->_class == npClass();
72}
73
74void NPJSObject::initialize(VM& vm, NPRuntimeObjectMap* objectMap, JSObject* jsObject)
75{
76    ASSERT(!m_objectMap);
77    ASSERT(!m_jsObject);
78
79    m_objectMap = objectMap;
80    m_jsObject.set(vm, jsObject);
81}
82
83static Identifier identifierFromIdentifierRep(ExecState* exec, IdentifierRep* identifierRep)
84{
85    ASSERT(identifierRep->isString());
86
87    const char* string = identifierRep->string();
88    int length = strlen(string);
89
90    return Identifier(exec, String::fromUTF8WithLatin1Fallback(string, length).impl());
91}
92
93bool NPJSObject::hasMethod(NPIdentifier methodName)
94{
95    IdentifierRep* identifierRep = static_cast<IdentifierRep*>(methodName);
96
97    if (!identifierRep->isString())
98        return false;
99
100    ExecState* exec = m_objectMap->globalExec();
101    if (!exec)
102        return false;
103
104    JSLockHolder lock(exec);
105
106    JSValue value = m_jsObject->get(exec, identifierFromIdentifierRep(exec, identifierRep));
107    exec->clearException();
108
109    CallData callData;
110    return getCallData(value, callData) != CallTypeNone;
111}
112
113bool NPJSObject::invoke(NPIdentifier methodName, const NPVariant* arguments, uint32_t argumentCount, NPVariant* result)
114{
115    IdentifierRep* identifierRep = static_cast<IdentifierRep*>(methodName);
116
117    if (!identifierRep->isString())
118        return false;
119
120    ExecState* exec = m_objectMap->globalExec();
121    if (!exec)
122        return false;
123
124    JSLockHolder lock(exec);
125
126    JSValue function = m_jsObject->get(exec, identifierFromIdentifierRep(exec, identifierRep));
127    return invoke(exec, m_objectMap->globalObject(), function, arguments, argumentCount, result);
128}
129
130bool NPJSObject::invokeDefault(const NPVariant* arguments, uint32_t argumentCount, NPVariant* result)
131{
132    ExecState* exec = m_objectMap->globalExec();
133    if (!exec)
134        return false;
135
136    JSLockHolder lock(exec);
137
138    JSValue function = m_jsObject.get();
139    return invoke(exec, m_objectMap->globalObject(), function, arguments, argumentCount, result);
140}
141
142bool NPJSObject::hasProperty(NPIdentifier identifier)
143{
144    IdentifierRep* identifierRep = static_cast<IdentifierRep*>(identifier);
145
146    ExecState* exec = m_objectMap->globalExec();
147    if (!exec)
148        return false;
149
150    JSLockHolder lock(exec);
151
152    bool result;
153    if (identifierRep->isString())
154        result = m_jsObject->hasProperty(exec, identifierFromIdentifierRep(exec, identifierRep));
155    else
156        result = m_jsObject->hasProperty(exec, identifierRep->number());
157
158    exec->clearException();
159    return result;
160}
161
162bool NPJSObject::getProperty(NPIdentifier propertyName, NPVariant* result)
163{
164    IdentifierRep* identifierRep = static_cast<IdentifierRep*>(propertyName);
165
166    ExecState* exec = m_objectMap->globalExec();
167    if (!exec)
168        return false;
169
170    JSLockHolder lock(exec);
171    JSValue jsResult;
172    if (identifierRep->isString())
173        jsResult = m_jsObject->get(exec, identifierFromIdentifierRep(exec, identifierRep));
174    else
175        jsResult = m_jsObject->get(exec, identifierRep->number());
176
177    m_objectMap->convertJSValueToNPVariant(exec, jsResult, *result);
178    exec->clearException();
179    return true;
180}
181
182bool NPJSObject::setProperty(NPIdentifier propertyName, const NPVariant* value)
183{
184    IdentifierRep* identifierRep = static_cast<IdentifierRep*>(propertyName);
185
186    ExecState* exec = m_objectMap->globalExec();
187    if (!exec)
188        return false;
189
190    JSLockHolder lock(exec);
191
192    JSValue jsValue = m_objectMap->convertNPVariantToJSValue(exec, m_objectMap->globalObject(), *value);
193    if (identifierRep->isString()) {
194        PutPropertySlot slot(m_jsObject.get());
195        m_jsObject->methodTable()->put(m_jsObject.get(), exec, identifierFromIdentifierRep(exec, identifierRep), jsValue, slot);
196    } else
197        m_jsObject->methodTable()->putByIndex(m_jsObject.get(), exec, identifierRep->number(), jsValue, false);
198    exec->clearException();
199
200    return true;
201}
202
203bool NPJSObject::removeProperty(NPIdentifier propertyName)
204{
205    IdentifierRep* identifierRep = static_cast<IdentifierRep*>(propertyName);
206
207    ExecState* exec = m_objectMap->globalExec();
208    if (!exec)
209        return false;
210
211    JSLockHolder lock(exec);
212    if (identifierRep->isString()) {
213        Identifier identifier = identifierFromIdentifierRep(exec, identifierRep);
214
215        if (!m_jsObject->hasProperty(exec, identifier)) {
216            exec->clearException();
217            return false;
218        }
219
220        m_jsObject->methodTable()->deleteProperty(m_jsObject.get(), exec, identifier);
221    } else {
222        if (!m_jsObject->hasProperty(exec, identifierRep->number())) {
223            exec->clearException();
224            return false;
225        }
226
227        m_jsObject->methodTable()->deletePropertyByIndex(m_jsObject.get(), exec, identifierRep->number());
228    }
229
230    exec->clearException();
231    return true;
232}
233
234bool NPJSObject::enumerate(NPIdentifier** identifiers, uint32_t* identifierCount)
235{
236    ExecState* exec = m_objectMap->globalExec();
237    if (!exec)
238        return false;
239
240    JSLockHolder lock(exec);
241
242    PropertyNameArray propertyNames(exec);
243    m_jsObject->methodTable()->getPropertyNames(m_jsObject.get(), exec, propertyNames, ExcludeDontEnumProperties);
244
245    NPIdentifier* nameIdentifiers = npnMemNewArray<NPIdentifier>(propertyNames.size());
246
247    for (size_t i = 0; i < propertyNames.size(); ++i)
248        nameIdentifiers[i] = static_cast<NPIdentifier>(IdentifierRep::get(propertyNames[i].string().utf8().data()));
249
250    *identifiers = nameIdentifiers;
251    *identifierCount = propertyNames.size();
252
253    return true;
254}
255
256bool NPJSObject::construct(const NPVariant* arguments, uint32_t argumentCount, NPVariant* result)
257{
258    ExecState* exec = m_objectMap->globalExec();
259    if (!exec)
260        return false;
261
262    JSLockHolder lock(exec);
263
264    ConstructData constructData;
265    ConstructType constructType = getConstructData(m_jsObject.get(), constructData);
266    if (constructType == ConstructTypeNone)
267        return false;
268
269    // Convert the passed in arguments.
270    MarkedArgumentBuffer argumentList;
271    for (uint32_t i = 0; i < argumentCount; ++i)
272        argumentList.append(m_objectMap->convertNPVariantToJSValue(exec, m_objectMap->globalObject(), arguments[i]));
273
274    JSValue value = JSC::construct(exec, m_jsObject.get(), constructType, constructData, argumentList);
275
276    // Convert and return the new object.
277    m_objectMap->convertJSValueToNPVariant(exec, value, *result);
278    exec->clearException();
279
280    return true;
281}
282
283bool NPJSObject::invoke(ExecState* exec, JSGlobalObject* globalObject, JSValue function, const NPVariant* arguments, uint32_t argumentCount, NPVariant* result)
284{
285    CallData callData;
286    CallType callType = getCallData(function, callData);
287    if (callType == CallTypeNone)
288        return false;
289
290    // Convert the passed in arguments.
291    MarkedArgumentBuffer argumentList;
292    for (uint32_t i = 0; i < argumentCount; ++i)
293        argumentList.append(m_objectMap->convertNPVariantToJSValue(exec, globalObject, arguments[i]));
294
295    JSValue value = JSC::call(exec, function, callType, callData, m_jsObject.get(), argumentList);
296
297    // Convert and return the result of the function call.
298    m_objectMap->convertJSValueToNPVariant(exec, value, *result);
299    exec->clearException();
300
301    return true;
302}
303
304NPClass* NPJSObject::npClass()
305{
306    static NPClass npClass = {
307        NP_CLASS_STRUCT_VERSION,
308        NP_Allocate,
309        NP_Deallocate,
310        0,
311        NP_HasMethod,
312        NP_Invoke,
313        NP_InvokeDefault,
314        NP_HasProperty,
315        NP_GetProperty,
316        NP_SetProperty,
317        NP_RemoveProperty,
318        NP_Enumerate,
319        NP_Construct
320    };
321
322    return &npClass;
323}
324
325NPObject* NPJSObject::NP_Allocate(NPP npp, NPClass*)
326{
327    ASSERT_UNUSED(npp, !npp);
328
329    return new NPJSObject;
330}
331
332void NPJSObject::NP_Deallocate(NPObject* npObject)
333{
334    NPJSObject* npJSObject = toNPJSObject(npObject);
335    delete npJSObject;
336}
337
338bool NPJSObject::NP_HasMethod(NPObject* npObject, NPIdentifier methodName)
339{
340    return toNPJSObject(npObject)->hasMethod(methodName);
341}
342
343bool NPJSObject::NP_Invoke(NPObject* npObject, NPIdentifier methodName, const NPVariant* arguments, uint32_t argumentCount, NPVariant* result)
344{
345    return toNPJSObject(npObject)->invoke(methodName, arguments, argumentCount, result);
346}
347
348bool NPJSObject::NP_InvokeDefault(NPObject* npObject, const NPVariant* arguments, uint32_t argumentCount, NPVariant* result)
349{
350    return toNPJSObject(npObject)->invokeDefault(arguments, argumentCount, result);
351}
352
353bool NPJSObject::NP_HasProperty(NPObject* npObject, NPIdentifier propertyName)
354{
355    return toNPJSObject(npObject)->hasProperty(propertyName);
356}
357
358bool NPJSObject::NP_GetProperty(NPObject* npObject, NPIdentifier propertyName, NPVariant* result)
359{
360    return toNPJSObject(npObject)->getProperty(propertyName, result);
361}
362
363bool NPJSObject::NP_SetProperty(NPObject* npObject, NPIdentifier propertyName, const NPVariant* value)
364{
365    return toNPJSObject(npObject)->setProperty(propertyName, value);
366}
367
368bool NPJSObject::NP_RemoveProperty(NPObject* npObject, NPIdentifier propertyName)
369{
370    return toNPJSObject(npObject)->removeProperty(propertyName);
371}
372
373bool NPJSObject::NP_Enumerate(NPObject* npObject, NPIdentifier** identifiers, uint32_t* identifierCount)
374{
375    return toNPJSObject(npObject)->enumerate(identifiers, identifierCount);
376}
377
378bool NPJSObject::NP_Construct(NPObject* npObject, const NPVariant* arguments, uint32_t argumentCount, NPVariant* result)
379{
380    return toNPJSObject(npObject)->construct(arguments, argumentCount, result);
381}
382
383} // namespace WebKit
384
385#endif // ENABLE(NETSCAPE_PLUGIN_API)
386