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/CatchScope.h>
35#include <JavaScriptCore/JSCInlines.h>
36#include <JavaScriptCore/JSCellInlines.h>
37#include <JavaScriptCore/JSLock.h>
38#include <JavaScriptCore/JSObject.h>
39#include <JavaScriptCore/StrongInlines.h>
40#include <WebCore/Frame.h>
41#include <WebCore/IdentifierRep.h>
42#include <wtf/text/WTFString.h>
43
44namespace WebKit {
45using namespace JSC;
46using namespace WebCore;
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>(vm));
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::fromString(exec, String::fromUTF8WithLatin1Fallback(string, length));
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 VM& vm = exec->vm();
105 JSLockHolder lock(vm);
106 auto scope = DECLARE_CATCH_SCOPE(vm);
107
108 JSValue value = m_jsObject->get(exec, identifierFromIdentifierRep(exec, identifierRep));
109 scope.clearException();
110
111 CallData callData;
112 return getCallData(vm, value, callData) != CallType::None;
113}
114
115bool NPJSObject::invoke(NPIdentifier methodName, const NPVariant* arguments, uint32_t argumentCount, NPVariant* result)
116{
117 IdentifierRep* identifierRep = static_cast<IdentifierRep*>(methodName);
118
119 if (!identifierRep->isString())
120 return false;
121
122 ExecState* exec = m_objectMap->globalExec();
123 if (!exec)
124 return false;
125
126 JSLockHolder lock(exec);
127
128 JSValue function = m_jsObject->get(exec, identifierFromIdentifierRep(exec, identifierRep));
129 return invoke(exec, m_objectMap->globalObject(), function, arguments, argumentCount, result);
130}
131
132bool NPJSObject::invokeDefault(const NPVariant* arguments, uint32_t argumentCount, NPVariant* result)
133{
134 ExecState* exec = m_objectMap->globalExec();
135 if (!exec)
136 return false;
137
138 JSLockHolder lock(exec);
139
140 JSValue function = m_jsObject.get();
141 return invoke(exec, m_objectMap->globalObject(), function, arguments, argumentCount, result);
142}
143
144bool NPJSObject::hasProperty(NPIdentifier identifier)
145{
146 IdentifierRep* identifierRep = static_cast<IdentifierRep*>(identifier);
147
148 ExecState* exec = m_objectMap->globalExec();
149 if (!exec)
150 return false;
151
152 VM& vm = exec->vm();
153 JSLockHolder lock(vm);
154 auto scope = DECLARE_CATCH_SCOPE(vm);
155
156 bool result;
157 if (identifierRep->isString())
158 result = m_jsObject->hasProperty(exec, identifierFromIdentifierRep(exec, identifierRep));
159 else
160 result = m_jsObject->hasProperty(exec, identifierRep->number());
161
162 scope.clearException();
163 return result;
164}
165
166bool NPJSObject::getProperty(NPIdentifier propertyName, NPVariant* result)
167{
168 IdentifierRep* identifierRep = static_cast<IdentifierRep*>(propertyName);
169
170 ExecState* exec = m_objectMap->globalExec();
171 if (!exec)
172 return false;
173
174 VM& vm = exec->vm();
175 JSLockHolder lock(vm);
176 auto scope = DECLARE_CATCH_SCOPE(vm);
177
178 JSValue jsResult;
179 if (identifierRep->isString())
180 jsResult = m_jsObject->get(exec, identifierFromIdentifierRep(exec, identifierRep));
181 else
182 jsResult = m_jsObject->get(exec, identifierRep->number());
183
184 m_objectMap->convertJSValueToNPVariant(exec, jsResult, *result);
185 scope.clearException();
186 return true;
187}
188
189bool NPJSObject::setProperty(NPIdentifier propertyName, const NPVariant* value)
190{
191 IdentifierRep* identifierRep = static_cast<IdentifierRep*>(propertyName);
192
193 ExecState* exec = m_objectMap->globalExec();
194 if (!exec)
195 return false;
196
197 VM& vm = exec->vm();
198 JSLockHolder lock(vm);
199 auto scope = DECLARE_CATCH_SCOPE(vm);
200
201 JSValue jsValue = m_objectMap->convertNPVariantToJSValue(exec, m_objectMap->globalObject(), *value);
202 if (identifierRep->isString()) {
203 PutPropertySlot slot(m_jsObject.get());
204 m_jsObject->methodTable(vm)->put(m_jsObject.get(), exec, identifierFromIdentifierRep(exec, identifierRep), jsValue, slot);
205 } else
206 m_jsObject->methodTable(vm)->putByIndex(m_jsObject.get(), exec, identifierRep->number(), jsValue, false);
207 scope.clearException();
208
209 return true;
210}
211
212bool NPJSObject::removeProperty(NPIdentifier propertyName)
213{
214 IdentifierRep* identifierRep = static_cast<IdentifierRep*>(propertyName);
215
216 ExecState* exec = m_objectMap->globalExec();
217 if (!exec)
218 return false;
219
220 VM& vm = exec->vm();
221 JSLockHolder lock(vm);
222 auto scope = DECLARE_CATCH_SCOPE(vm);
223
224 if (identifierRep->isString()) {
225 Identifier identifier = identifierFromIdentifierRep(exec, identifierRep);
226
227 if (!m_jsObject->hasProperty(exec, identifier)) {
228 scope.clearException();
229 return false;
230 }
231
232 m_jsObject->methodTable(vm)->deleteProperty(m_jsObject.get(), exec, identifier);
233 } else {
234 if (!m_jsObject->hasProperty(exec, identifierRep->number())) {
235 scope.clearException();
236 return false;
237 }
238
239 m_jsObject->methodTable(vm)->deletePropertyByIndex(m_jsObject.get(), exec, identifierRep->number());
240 }
241
242 scope.clearException();
243 return true;
244}
245
246bool NPJSObject::enumerate(NPIdentifier** identifiers, uint32_t* identifierCount)
247{
248 ExecState* exec = m_objectMap->globalExec();
249 if (!exec)
250 return false;
251
252 VM& vm = exec->vm();
253 JSLockHolder lock(vm);
254
255 PropertyNameArray propertyNames(&vm, PropertyNameMode::Strings, PrivateSymbolMode::Exclude);
256 m_jsObject->methodTable(vm)->getPropertyNames(m_jsObject.get(), exec, propertyNames, EnumerationMode());
257
258 NPIdentifier* nameIdentifiers = npnMemNewArray<NPIdentifier>(propertyNames.size());
259
260 for (size_t i = 0; i < propertyNames.size(); ++i)
261 nameIdentifiers[i] = static_cast<NPIdentifier>(IdentifierRep::get(propertyNames[i].string().utf8().data()));
262
263 *identifiers = nameIdentifiers;
264 *identifierCount = propertyNames.size();
265
266 return true;
267}
268
269bool NPJSObject::construct(const NPVariant* arguments, uint32_t argumentCount, NPVariant* result)
270{
271 ExecState* exec = m_objectMap->globalExec();
272 if (!exec)
273 return false;
274
275 VM& vm = exec->vm();
276 JSLockHolder lock(vm);
277 auto scope = DECLARE_CATCH_SCOPE(vm);
278
279 ConstructData constructData;
280 ConstructType constructType = getConstructData(vm, m_jsObject.get(), constructData);
281 if (constructType == ConstructType::None)
282 return false;
283
284 // Convert the passed in arguments.
285 MarkedArgumentBuffer argumentList;
286 for (uint32_t i = 0; i < argumentCount; ++i)
287 argumentList.append(m_objectMap->convertNPVariantToJSValue(exec, m_objectMap->globalObject(), arguments[i]));
288 RELEASE_ASSERT(!argumentList.hasOverflowed());
289
290 JSValue value = JSC::construct(exec, m_jsObject.get(), constructType, constructData, argumentList);
291
292 // Convert and return the new object.
293 m_objectMap->convertJSValueToNPVariant(exec, value, *result);
294 scope.clearException();
295
296 return true;
297}
298
299bool NPJSObject::invoke(ExecState* exec, JSGlobalObject* globalObject, JSValue function, const NPVariant* arguments, uint32_t argumentCount, NPVariant* result)
300{
301 VM& vm = exec->vm();
302 auto scope = DECLARE_CATCH_SCOPE(vm);
303
304 CallData callData;
305 CallType callType = getCallData(vm, function, callData);
306 if (callType == CallType::None)
307 return false;
308
309 // Convert the passed in arguments.
310 MarkedArgumentBuffer argumentList;
311 for (uint32_t i = 0; i < argumentCount; ++i)
312 argumentList.append(m_objectMap->convertNPVariantToJSValue(exec, globalObject, arguments[i]));
313 RELEASE_ASSERT(!argumentList.hasOverflowed());
314
315 JSValue value = JSC::call(exec, function, callType, callData, m_jsObject.get(), argumentList);
316
317 if (UNLIKELY(scope.exception())) {
318 scope.clearException();
319 return false;
320 }
321
322 // Convert and return the result of the function call.
323 m_objectMap->convertJSValueToNPVariant(exec, value, *result);
324
325 if (UNLIKELY(scope.exception())) {
326 scope.clearException();
327 return false;
328 }
329
330 return true;
331}
332
333NPClass* NPJSObject::npClass()
334{
335 static NPClass npClass = {
336 NP_CLASS_STRUCT_VERSION,
337 NP_Allocate,
338 NP_Deallocate,
339 0,
340 NP_HasMethod,
341 NP_Invoke,
342 NP_InvokeDefault,
343 NP_HasProperty,
344 NP_GetProperty,
345 NP_SetProperty,
346 NP_RemoveProperty,
347 NP_Enumerate,
348 NP_Construct
349 };
350
351 return &npClass;
352}
353
354NPObject* NPJSObject::NP_Allocate(NPP npp, NPClass*)
355{
356 ASSERT_UNUSED(npp, !npp);
357
358 return new NPJSObject;
359}
360
361void NPJSObject::NP_Deallocate(NPObject* npObject)
362{
363 NPJSObject* npJSObject = toNPJSObject(npObject);
364 delete npJSObject;
365}
366
367bool NPJSObject::NP_HasMethod(NPObject* npObject, NPIdentifier methodName)
368{
369 return toNPJSObject(npObject)->hasMethod(methodName);
370}
371
372bool NPJSObject::NP_Invoke(NPObject* npObject, NPIdentifier methodName, const NPVariant* arguments, uint32_t argumentCount, NPVariant* result)
373{
374 return toNPJSObject(npObject)->invoke(methodName, arguments, argumentCount, result);
375}
376
377bool NPJSObject::NP_InvokeDefault(NPObject* npObject, const NPVariant* arguments, uint32_t argumentCount, NPVariant* result)
378{
379 return toNPJSObject(npObject)->invokeDefault(arguments, argumentCount, result);
380}
381
382bool NPJSObject::NP_HasProperty(NPObject* npObject, NPIdentifier propertyName)
383{
384 return toNPJSObject(npObject)->hasProperty(propertyName);
385}
386
387bool NPJSObject::NP_GetProperty(NPObject* npObject, NPIdentifier propertyName, NPVariant* result)
388{
389 return toNPJSObject(npObject)->getProperty(propertyName, result);
390}
391
392bool NPJSObject::NP_SetProperty(NPObject* npObject, NPIdentifier propertyName, const NPVariant* value)
393{
394 return toNPJSObject(npObject)->setProperty(propertyName, value);
395}
396
397bool NPJSObject::NP_RemoveProperty(NPObject* npObject, NPIdentifier propertyName)
398{
399 return toNPJSObject(npObject)->removeProperty(propertyName);
400}
401
402bool NPJSObject::NP_Enumerate(NPObject* npObject, NPIdentifier** identifiers, uint32_t* identifierCount)
403{
404 return toNPJSObject(npObject)->enumerate(identifiers, identifierCount);
405}
406
407bool NPJSObject::NP_Construct(NPObject* npObject, const NPVariant* arguments, uint32_t argumentCount, NPVariant* result)
408{
409 return toNPJSObject(npObject)->construct(arguments, argumentCount, result);
410}
411
412} // namespace WebKit
413
414#endif // ENABLE(NETSCAPE_PLUGIN_API)
415