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