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#include "JSNPObject.h"
30#include "NPRuntimeObjectMap.h"
31#include "NPRuntimeUtilities.h"
32#include "PluginView.h"
33#include <JavaScriptCore/JSLock.h>
34#include <JavaScriptCore/JSObject.h>
35#include <WebCore/Frame.h>
36#include <WebCore/IdentifierRep.h>
37#include <WebCore/NotImplemented.h>
38#include <wtf/text/WTFString.h>
39
40using namespace JSC;
41using namespace WebCore;
42
43namespace WebKit {
44
45NPJSObject* NPJSObject::create(JSGlobalData& globalData, NPRuntimeObjectMap* objectMap, JSObject* jsObject)
46{
47    // We should never have a JSNPObject inside an NPJSObject.
48    ASSERT(!jsObject->inherits(&JSNPObject::s_info));
49
50    NPJSObject* npJSObject = toNPJSObject(createNPObject(0, npClass()));
51    npJSObject->initialize(globalData, objectMap, jsObject);
52
53    return npJSObject;
54}
55
56NPJSObject::NPJSObject()
57    : m_objectMap(0)
58{
59}
60
61NPJSObject::~NPJSObject()
62{
63    m_objectMap->npJSObjectDestroyed(this);
64}
65
66bool NPJSObject::isNPJSObject(NPObject* npObject)
67{
68    return npObject->_class == npClass();
69}
70
71void NPJSObject::initialize(JSGlobalData& globalData, NPRuntimeObjectMap* objectMap, JSObject* jsObject)
72{
73    ASSERT(!m_objectMap);
74    ASSERT(!m_jsObject);
75
76    m_objectMap = objectMap;
77    m_jsObject.set(globalData, jsObject);
78}
79
80static Identifier identifierFromIdentifierRep(ExecState* exec, IdentifierRep* identifierRep)
81{
82    ASSERT(identifierRep->isString());
83
84    const char* string = identifierRep->string();
85    int length = strlen(string);
86
87    return Identifier(exec, String::fromUTF8WithLatin1Fallback(string, length).impl());
88}
89
90bool NPJSObject::hasMethod(NPIdentifier methodName)
91{
92    IdentifierRep* identifierRep = static_cast<IdentifierRep*>(methodName);
93
94    if (!identifierRep->isString())
95        return false;
96
97    ExecState* exec = m_objectMap->globalExec();
98    if (!exec)
99        return false;
100
101    JSLock lock(SilenceAssertionsOnly);
102
103    JSValue value = m_jsObject->get(exec, identifierFromIdentifierRep(exec, identifierRep));
104    exec->clearException();
105
106    CallData callData;
107    return getCallData(value, callData) != CallTypeNone;
108}
109
110bool NPJSObject::invoke(NPIdentifier methodName, const NPVariant* arguments, uint32_t argumentCount, NPVariant* result)
111{
112    IdentifierRep* identifierRep = static_cast<IdentifierRep*>(methodName);
113
114    if (!identifierRep->isString())
115        return false;
116
117    ExecState* exec = m_objectMap->globalExec();
118    if (!exec)
119        return false;
120
121    JSLock lock(SilenceAssertionsOnly);
122
123    JSValue function = m_jsObject->get(exec, identifierFromIdentifierRep(exec, identifierRep));
124    return invoke(exec, m_objectMap->globalObject(), function, arguments, argumentCount, result);
125}
126
127bool NPJSObject::invokeDefault(const NPVariant* arguments, uint32_t argumentCount, NPVariant* result)
128{
129    ExecState* exec = m_objectMap->globalExec();
130    if (!exec)
131        return false;
132
133    JSLock lock(SilenceAssertionsOnly);
134
135    JSValue function = m_jsObject.get();
136    return invoke(exec, m_objectMap->globalObject(), function, arguments, argumentCount, result);
137}
138
139bool NPJSObject::hasProperty(NPIdentifier identifier)
140{
141    IdentifierRep* identifierRep = static_cast<IdentifierRep*>(identifier);
142
143    ExecState* exec = m_objectMap->globalExec();
144    if (!exec)
145        return false;
146
147    JSLock lock(SilenceAssertionsOnly);
148
149    bool result;
150    if (identifierRep->isString())
151        result = m_jsObject->hasProperty(exec, identifierFromIdentifierRep(exec, identifierRep));
152    else
153        result = m_jsObject->hasProperty(exec, identifierRep->number());
154
155    exec->clearException();
156    return result;
157}
158
159bool NPJSObject::getProperty(NPIdentifier propertyName, NPVariant* result)
160{
161    IdentifierRep* identifierRep = static_cast<IdentifierRep*>(propertyName);
162
163    ExecState* exec = m_objectMap->globalExec();
164    if (!exec)
165        return false;
166
167    JSLock lock(SilenceAssertionsOnly);
168    JSValue jsResult;
169    if (identifierRep->isString())
170        jsResult = m_jsObject->get(exec, identifierFromIdentifierRep(exec, identifierRep));
171    else
172        jsResult = m_jsObject->get(exec, identifierRep->number());
173
174    m_objectMap->convertJSValueToNPVariant(exec, jsResult, *result);
175    exec->clearException();
176    return true;
177}
178
179bool NPJSObject::setProperty(NPIdentifier propertyName, const NPVariant* value)
180{
181    IdentifierRep* identifierRep = static_cast<IdentifierRep*>(propertyName);
182
183    ExecState* exec = m_objectMap->globalExec();
184    if (!exec)
185        return false;
186
187    JSLock lock(SilenceAssertionsOnly);
188
189    JSValue jsValue = m_objectMap->convertNPVariantToJSValue(exec, m_objectMap->globalObject(), *value);
190    if (identifierRep->isString()) {
191        PutPropertySlot slot;
192        m_jsObject->put(exec, identifierFromIdentifierRep(exec, identifierRep), jsValue, slot);
193    } else
194        m_jsObject->put(exec, identifierRep->number(), jsValue);
195    exec->clearException();
196
197    return true;
198}
199
200bool NPJSObject::removeProperty(NPIdentifier propertyName)
201{
202    IdentifierRep* identifierRep = static_cast<IdentifierRep*>(propertyName);
203
204    ExecState* exec = m_objectMap->globalExec();
205    if (!exec)
206        return false;
207
208    JSLock lock(SilenceAssertionsOnly);
209    if (identifierRep->isString()) {
210        Identifier identifier = identifierFromIdentifierRep(exec, identifierRep);
211
212        if (!m_jsObject->hasProperty(exec, identifier)) {
213            exec->clearException();
214            return false;
215        }
216
217        m_jsObject->deleteProperty(exec, identifier);
218    } else {
219        if (!m_jsObject->hasProperty(exec, identifierRep->number())) {
220            exec->clearException();
221            return false;
222        }
223
224        m_jsObject->deleteProperty(exec, identifierRep->number());
225    }
226
227    exec->clearException();
228    return true;
229}
230
231bool NPJSObject::enumerate(NPIdentifier** identifiers, uint32_t* identifierCount)
232{
233    ExecState* exec = m_objectMap->globalExec();
234    if (!exec)
235        return false;
236
237    JSLock lock(SilenceAssertionsOnly);
238
239    PropertyNameArray propertyNames(exec);
240    m_jsObject->getPropertyNames(exec, propertyNames);
241
242    NPIdentifier* nameIdentifiers = npnMemNewArray<NPIdentifier>(propertyNames.size());
243
244    for (size_t i = 0; i < propertyNames.size(); ++i)
245        nameIdentifiers[i] = static_cast<NPIdentifier>(IdentifierRep::get(propertyNames[i].ustring().utf8().data()));
246
247    *identifiers = nameIdentifiers;
248    *identifierCount = propertyNames.size();
249
250    return true;
251}
252
253bool NPJSObject::construct(const NPVariant* arguments, uint32_t argumentCount, NPVariant* result)
254{
255    ExecState* exec = m_objectMap->globalExec();
256    if (!exec)
257        return false;
258
259    JSLock lock(SilenceAssertionsOnly);
260
261    ConstructData constructData;
262    ConstructType constructType = getConstructData(m_jsObject.get(), constructData);
263    if (constructType == ConstructTypeNone)
264        return false;
265
266    // Convert the passed in arguments.
267    MarkedArgumentBuffer argumentList;
268    for (uint32_t i = 0; i < argumentCount; ++i)
269        argumentList.append(m_objectMap->convertNPVariantToJSValue(exec, m_objectMap->globalObject(), arguments[i]));
270
271    exec->globalData().timeoutChecker.start();
272    JSValue value = JSC::construct(exec, m_jsObject.get(), constructType, constructData, argumentList);
273    exec->globalData().timeoutChecker.stop();
274
275    // Convert and return the new object.
276    m_objectMap->convertJSValueToNPVariant(exec, value, *result);
277    exec->clearException();
278
279    return true;
280}
281
282bool NPJSObject::invoke(ExecState* exec, JSGlobalObject* globalObject, JSValue function, const NPVariant* arguments, uint32_t argumentCount, NPVariant* result)
283{
284    CallData callData;
285    CallType callType = getCallData(function, callData);
286    if (callType == CallTypeNone)
287        return false;
288
289    // Convert the passed in arguments.
290    MarkedArgumentBuffer argumentList;
291    for (uint32_t i = 0; i < argumentCount; ++i)
292        argumentList.append(m_objectMap->convertNPVariantToJSValue(exec, globalObject, arguments[i]));
293
294    exec->globalData().timeoutChecker.start();
295    JSValue value = JSC::call(exec, function, callType, callData, m_jsObject.get(), argumentList);
296    exec->globalData().timeoutChecker.stop();
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