1 /*
2 * Copyright (C) 2004, 2006 Apple Computer, 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 COMPUTER, 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 COMPUTER, 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 "NP_jsobject.h"
31
32 #include "PlatformString.h"
33 #include "PluginView.h"
34 #include "StringSourceProvider.h"
35 #include "c_utility.h"
36 #include "c_instance.h"
37 #include "IdentifierRep.h"
38 #include "JSDOMBinding.h"
39 #include "npruntime_impl.h"
40 #include "npruntime_priv.h"
41 #include "runtime_root.h"
42 #include <runtime/Error.h>
43 #include <runtime/JSGlobalObject.h>
44 #include <runtime/JSLock.h>
45 #include <runtime/PropertyNameArray.h>
46 #include <parser/SourceCode.h>
47 #include <runtime/Completion.h>
48 #include <runtime/Completion.h>
49
50 using namespace JSC;
51 using namespace JSC::Bindings;
52 using namespace WebCore;
53
54 class ObjectMap {
55 public:
get(RootObject * rootObject,JSObject * jsObject)56 NPObject* get(RootObject* rootObject, JSObject* jsObject)
57 {
58 return m_map.get(rootObject).get(jsObject);
59 }
60
add(RootObject * rootObject,JSObject * jsObject,NPObject * npObject)61 void add(RootObject* rootObject, JSObject* jsObject, NPObject* npObject)
62 {
63 HashMap<RootObject*, JSToNPObjectMap>::iterator iter = m_map.find(rootObject);
64 if (iter == m_map.end()) {
65 rootObject->addInvalidationCallback(&m_invalidationCallback);
66 iter = m_map.add(rootObject, JSToNPObjectMap()).first;
67 }
68
69 ASSERT(iter->second.find(jsObject) == iter->second.end());
70 iter->second.add(jsObject, npObject);
71 }
72
remove(RootObject * rootObject)73 void remove(RootObject* rootObject)
74 {
75 HashMap<RootObject*, JSToNPObjectMap>::iterator iter = m_map.find(rootObject);
76 ASSERT(iter != m_map.end());
77 m_map.remove(iter);
78 }
79
remove(RootObject * rootObject,JSObject * jsObject)80 void remove(RootObject* rootObject, JSObject* jsObject)
81 {
82 HashMap<RootObject*, JSToNPObjectMap>::iterator iter = m_map.find(rootObject);
83 ASSERT(iter != m_map.end());
84 ASSERT(iter->second.find(jsObject) != iter->second.end());
85
86 iter->second.remove(jsObject);
87 }
88
89 private:
90 struct RootObjectInvalidationCallback : public RootObject::InvalidationCallback {
91 virtual void operator()(RootObject*);
92 };
93 RootObjectInvalidationCallback m_invalidationCallback;
94
95 // JSObjects are protected by RootObject.
96 typedef HashMap<JSObject*, NPObject*> JSToNPObjectMap;
97 HashMap<RootObject*, JSToNPObjectMap> m_map;
98 };
99
100
objectMap()101 static ObjectMap& objectMap()
102 {
103 DEFINE_STATIC_LOCAL(ObjectMap, map, ());
104 return map;
105 }
106
operator ()(RootObject * rootObject)107 void ObjectMap::RootObjectInvalidationCallback::operator()(RootObject* rootObject)
108 {
109 objectMap().remove(rootObject);
110 }
111
getListFromVariantArgs(ExecState * exec,const NPVariant * args,unsigned argCount,RootObject * rootObject,MarkedArgumentBuffer & aList)112 static void getListFromVariantArgs(ExecState* exec, const NPVariant* args, unsigned argCount, RootObject* rootObject, MarkedArgumentBuffer& aList)
113 {
114 for (unsigned i = 0; i < argCount; ++i)
115 aList.append(convertNPVariantToValue(exec, &args[i], rootObject));
116 }
117
jsAllocate(NPP,NPClass *)118 static NPObject* jsAllocate(NPP, NPClass*)
119 {
120 return static_cast<NPObject*>(malloc(sizeof(JavaScriptObject)));
121 }
122
jsDeallocate(NPObject * npObj)123 static void jsDeallocate(NPObject* npObj)
124 {
125 JavaScriptObject* obj = reinterpret_cast<JavaScriptObject*>(npObj);
126
127 if (obj->rootObject && obj->rootObject->isValid()) {
128 objectMap().remove(obj->rootObject, obj->imp);
129 obj->rootObject->gcUnprotect(obj->imp);
130 }
131
132 if (obj->rootObject)
133 obj->rootObject->deref();
134
135 free(obj);
136 }
137
138 static NPClass javascriptClass = { 1, jsAllocate, jsDeallocate, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 };
139 static NPClass noScriptClass = { 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 };
140
141 NPClass* NPScriptObjectClass = &javascriptClass;
142 static NPClass* NPNoScriptObjectClass = &noScriptClass;
143
_NPN_CreateScriptObject(NPP npp,JSObject * imp,PassRefPtr<RootObject> rootObject)144 NPObject* _NPN_CreateScriptObject(NPP npp, JSObject* imp, PassRefPtr<RootObject> rootObject)
145 {
146 if (NPObject* object = objectMap().get(rootObject.get(), imp))
147 return _NPN_RetainObject(object);
148
149 JavaScriptObject* obj = reinterpret_cast<JavaScriptObject*>(_NPN_CreateObject(npp, NPScriptObjectClass));
150
151 obj->rootObject = rootObject.releaseRef();
152
153 if (obj->rootObject) {
154 obj->rootObject->gcProtect(imp);
155 objectMap().add(obj->rootObject, imp, reinterpret_cast<NPObject*>(obj));
156 }
157
158 obj->imp = imp;
159
160 return reinterpret_cast<NPObject*>(obj);
161 }
162
_NPN_CreateNoScriptObject(void)163 NPObject* _NPN_CreateNoScriptObject(void)
164 {
165 return _NPN_CreateObject(0, NPNoScriptObjectClass);
166 }
167
_NPN_InvokeDefault(NPP,NPObject * o,const NPVariant * args,uint32_t argCount,NPVariant * result)168 bool _NPN_InvokeDefault(NPP, NPObject* o, const NPVariant* args, uint32_t argCount, NPVariant* result)
169 {
170 if (o->_class == NPScriptObjectClass) {
171 JavaScriptObject* obj = reinterpret_cast<JavaScriptObject*>(o);
172
173 VOID_TO_NPVARIANT(*result);
174
175 // Lookup the function object.
176 RootObject* rootObject = obj->rootObject;
177 if (!rootObject || !rootObject->isValid())
178 return false;
179
180 ExecState* exec = rootObject->globalObject()->globalExec();
181 JSLock lock(SilenceAssertionsOnly);
182
183 // Call the function object.
184 JSValue function = obj->imp;
185 CallData callData;
186 CallType callType = getCallData(function, callData);
187 if (callType == CallTypeNone)
188 return false;
189
190 MarkedArgumentBuffer argList;
191 getListFromVariantArgs(exec, args, argCount, rootObject, argList);
192 RefPtr<JSGlobalData> globalData(&exec->globalData());
193 globalData->timeoutChecker.start();
194 JSValue resultV = JSC::call(exec, function, callType, callData, function, argList);
195 globalData->timeoutChecker.stop();
196
197 // Convert and return the result of the function call.
198 convertValueToNPVariant(exec, resultV, result);
199 exec->clearException();
200 return true;
201 }
202
203 if (o->_class->invokeDefault)
204 return o->_class->invokeDefault(o, args, argCount, result);
205 VOID_TO_NPVARIANT(*result);
206 return true;
207 }
208
_NPN_Invoke(NPP npp,NPObject * o,NPIdentifier methodName,const NPVariant * args,uint32_t argCount,NPVariant * result)209 bool _NPN_Invoke(NPP npp, NPObject* o, NPIdentifier methodName, const NPVariant* args, uint32_t argCount, NPVariant* result)
210 {
211 if (o->_class == NPScriptObjectClass) {
212 JavaScriptObject* obj = reinterpret_cast<JavaScriptObject*>(o);
213
214 IdentifierRep* i = static_cast<IdentifierRep*>(methodName);
215 if (!i->isString())
216 return false;
217
218 // Special case the "eval" method.
219 if (methodName == _NPN_GetStringIdentifier("eval")) {
220 if (argCount != 1)
221 return false;
222 if (args[0].type != NPVariantType_String)
223 return false;
224 return _NPN_Evaluate(npp, o, const_cast<NPString*>(&args[0].value.stringValue), result);
225 }
226
227 // Look up the function object.
228 RootObject* rootObject = obj->rootObject;
229 if (!rootObject || !rootObject->isValid())
230 return false;
231 ExecState* exec = rootObject->globalObject()->globalExec();
232 JSLock lock(SilenceAssertionsOnly);
233 JSValue function = obj->imp->get(exec, identifierFromNPIdentifier(exec, i->string()));
234 CallData callData;
235 CallType callType = getCallData(function, callData);
236 if (callType == CallTypeNone)
237 return false;
238
239 // Call the function object.
240 MarkedArgumentBuffer argList;
241 getListFromVariantArgs(exec, args, argCount, rootObject, argList);
242 RefPtr<JSGlobalData> globalData(&exec->globalData());
243 globalData->timeoutChecker.start();
244 JSValue resultV = JSC::call(exec, function, callType, callData, obj->imp, argList);
245 globalData->timeoutChecker.stop();
246
247 // Convert and return the result of the function call.
248 convertValueToNPVariant(exec, resultV, result);
249 exec->clearException();
250 return true;
251 }
252
253 if (o->_class->invoke)
254 return o->_class->invoke(o, methodName, args, argCount, result);
255
256 VOID_TO_NPVARIANT(*result);
257 return true;
258 }
259
_NPN_Evaluate(NPP instance,NPObject * o,NPString * s,NPVariant * variant)260 bool _NPN_Evaluate(NPP instance, NPObject* o, NPString* s, NPVariant* variant)
261 {
262 if (o->_class == NPScriptObjectClass) {
263 JavaScriptObject* obj = reinterpret_cast<JavaScriptObject*>(o);
264
265 RootObject* rootObject = obj->rootObject;
266 if (!rootObject || !rootObject->isValid())
267 return false;
268
269 // There is a crash in Flash when evaluating a script that destroys the
270 // PluginView, so we destroy it asynchronously.
271 PluginView::keepAlive(instance);
272
273 ExecState* exec = rootObject->globalObject()->globalExec();
274 JSLock lock(SilenceAssertionsOnly);
275 String scriptString = convertNPStringToUTF16(s);
276 RefPtr<JSGlobalData> globalData(&exec->globalData());
277 globalData->timeoutChecker.start();
278 Completion completion = JSC::evaluate(rootObject->globalObject()->globalExec(), rootObject->globalObject()->globalScopeChain(), makeSource(scriptString), JSC::JSValue());
279 globalData->timeoutChecker.stop();
280 ComplType type = completion.complType();
281
282 JSValue result;
283 if (type == Normal) {
284 result = completion.value();
285 if (!result)
286 result = jsUndefined();
287 } else
288 result = jsUndefined();
289
290 convertValueToNPVariant(exec, result, variant);
291 exec->clearException();
292 return true;
293 }
294
295 VOID_TO_NPVARIANT(*variant);
296 return false;
297 }
298
_NPN_GetProperty(NPP,NPObject * o,NPIdentifier propertyName,NPVariant * variant)299 bool _NPN_GetProperty(NPP, NPObject* o, NPIdentifier propertyName, NPVariant* variant)
300 {
301 if (o->_class == NPScriptObjectClass) {
302 JavaScriptObject* obj = reinterpret_cast<JavaScriptObject*>(o);
303
304 RootObject* rootObject = obj->rootObject;
305 if (!rootObject || !rootObject->isValid())
306 return false;
307
308 ExecState* exec = rootObject->globalObject()->globalExec();
309 IdentifierRep* i = static_cast<IdentifierRep*>(propertyName);
310
311 JSLock lock(SilenceAssertionsOnly);
312 JSValue result;
313 if (i->isString())
314 result = obj->imp->get(exec, identifierFromNPIdentifier(exec, i->string()));
315 else
316 result = obj->imp->get(exec, i->number());
317
318 convertValueToNPVariant(exec, result, variant);
319 exec->clearException();
320 return true;
321 }
322
323 if (o->_class->hasProperty && o->_class->getProperty) {
324 if (o->_class->hasProperty(o, propertyName))
325 return o->_class->getProperty(o, propertyName, variant);
326 return false;
327 }
328
329 VOID_TO_NPVARIANT(*variant);
330 return false;
331 }
332
_NPN_SetProperty(NPP,NPObject * o,NPIdentifier propertyName,const NPVariant * variant)333 bool _NPN_SetProperty(NPP, NPObject* o, NPIdentifier propertyName, const NPVariant* variant)
334 {
335 if (o->_class == NPScriptObjectClass) {
336 JavaScriptObject* obj = reinterpret_cast<JavaScriptObject*>(o);
337
338 RootObject* rootObject = obj->rootObject;
339 if (!rootObject || !rootObject->isValid())
340 return false;
341
342 ExecState* exec = rootObject->globalObject()->globalExec();
343 JSLock lock(SilenceAssertionsOnly);
344 IdentifierRep* i = static_cast<IdentifierRep*>(propertyName);
345
346 if (i->isString()) {
347 PutPropertySlot slot;
348 obj->imp->put(exec, identifierFromNPIdentifier(exec, i->string()), convertNPVariantToValue(exec, variant, rootObject), slot);
349 } else
350 obj->imp->put(exec, i->number(), convertNPVariantToValue(exec, variant, rootObject));
351 exec->clearException();
352 return true;
353 }
354
355 if (o->_class->setProperty)
356 return o->_class->setProperty(o, propertyName, variant);
357
358 return false;
359 }
360
_NPN_RemoveProperty(NPP,NPObject * o,NPIdentifier propertyName)361 bool _NPN_RemoveProperty(NPP, NPObject* o, NPIdentifier propertyName)
362 {
363 if (o->_class == NPScriptObjectClass) {
364 JavaScriptObject* obj = reinterpret_cast<JavaScriptObject*>(o);
365
366 RootObject* rootObject = obj->rootObject;
367 if (!rootObject || !rootObject->isValid())
368 return false;
369
370 ExecState* exec = rootObject->globalObject()->globalExec();
371 IdentifierRep* i = static_cast<IdentifierRep*>(propertyName);
372 if (i->isString()) {
373 if (!obj->imp->hasProperty(exec, identifierFromNPIdentifier(exec, i->string()))) {
374 exec->clearException();
375 return false;
376 }
377 } else {
378 if (!obj->imp->hasProperty(exec, i->number())) {
379 exec->clearException();
380 return false;
381 }
382 }
383
384 JSLock lock(SilenceAssertionsOnly);
385 if (i->isString())
386 obj->imp->deleteProperty(exec, identifierFromNPIdentifier(exec, i->string()));
387 else
388 obj->imp->deleteProperty(exec, i->number());
389
390 exec->clearException();
391 return true;
392 }
393 return false;
394 }
395
_NPN_HasProperty(NPP,NPObject * o,NPIdentifier propertyName)396 bool _NPN_HasProperty(NPP, NPObject* o, NPIdentifier propertyName)
397 {
398 if (o->_class == NPScriptObjectClass) {
399 JavaScriptObject* obj = reinterpret_cast<JavaScriptObject*>(o);
400
401 RootObject* rootObject = obj->rootObject;
402 if (!rootObject || !rootObject->isValid())
403 return false;
404
405 ExecState* exec = rootObject->globalObject()->globalExec();
406 IdentifierRep* i = static_cast<IdentifierRep*>(propertyName);
407 JSLock lock(SilenceAssertionsOnly);
408 if (i->isString()) {
409 bool result = obj->imp->hasProperty(exec, identifierFromNPIdentifier(exec, i->string()));
410 exec->clearException();
411 return result;
412 }
413
414 bool result = obj->imp->hasProperty(exec, i->number());
415 exec->clearException();
416 return result;
417 }
418
419 if (o->_class->hasProperty)
420 return o->_class->hasProperty(o, propertyName);
421
422 return false;
423 }
424
_NPN_HasMethod(NPP,NPObject * o,NPIdentifier methodName)425 bool _NPN_HasMethod(NPP, NPObject* o, NPIdentifier methodName)
426 {
427 if (o->_class == NPScriptObjectClass) {
428 JavaScriptObject* obj = reinterpret_cast<JavaScriptObject*>(o);
429
430 IdentifierRep* i = static_cast<IdentifierRep*>(methodName);
431 if (!i->isString())
432 return false;
433
434 RootObject* rootObject = obj->rootObject;
435 if (!rootObject || !rootObject->isValid())
436 return false;
437
438 ExecState* exec = rootObject->globalObject()->globalExec();
439 JSLock lock(SilenceAssertionsOnly);
440 JSValue func = obj->imp->get(exec, identifierFromNPIdentifier(exec, i->string()));
441 exec->clearException();
442 return !func.isUndefined();
443 }
444
445 if (o->_class->hasMethod)
446 return o->_class->hasMethod(o, methodName);
447
448 return false;
449 }
450
_NPN_SetException(NPObject *,const NPUTF8 * message)451 void _NPN_SetException(NPObject*, const NPUTF8* message)
452 {
453 // Ignoring the NPObject param is consistent with the Mozilla implementation.
454 UString exception(message);
455 CInstance::setGlobalException(exception);
456 }
457
_NPN_Enumerate(NPP,NPObject * o,NPIdentifier ** identifier,uint32_t * count)458 bool _NPN_Enumerate(NPP, NPObject* o, NPIdentifier** identifier, uint32_t* count)
459 {
460 if (o->_class == NPScriptObjectClass) {
461 JavaScriptObject* obj = reinterpret_cast<JavaScriptObject*>(o);
462
463 RootObject* rootObject = obj->rootObject;
464 if (!rootObject || !rootObject->isValid())
465 return false;
466
467 ExecState* exec = rootObject->globalObject()->globalExec();
468 JSLock lock(SilenceAssertionsOnly);
469 PropertyNameArray propertyNames(exec);
470
471 obj->imp->getPropertyNames(exec, propertyNames);
472 unsigned size = static_cast<unsigned>(propertyNames.size());
473 // FIXME: This should really call NPN_MemAlloc but that's in WebKit
474 NPIdentifier* identifiers = static_cast<NPIdentifier*>(malloc(sizeof(NPIdentifier) * size));
475
476 for (unsigned i = 0; i < size; ++i)
477 identifiers[i] = _NPN_GetStringIdentifier(propertyNames[i].ustring().utf8().data());
478
479 *identifier = identifiers;
480 *count = size;
481
482 exec->clearException();
483 return true;
484 }
485
486 if (NP_CLASS_STRUCT_VERSION_HAS_ENUM(o->_class) && o->_class->enumerate)
487 return o->_class->enumerate(o, identifier, count);
488
489 return false;
490 }
491
_NPN_Construct(NPP,NPObject * o,const NPVariant * args,uint32_t argCount,NPVariant * result)492 bool _NPN_Construct(NPP, NPObject* o, const NPVariant* args, uint32_t argCount, NPVariant* result)
493 {
494 if (o->_class == NPScriptObjectClass) {
495 JavaScriptObject* obj = reinterpret_cast<JavaScriptObject*>(o);
496
497 VOID_TO_NPVARIANT(*result);
498
499 // Lookup the constructor object.
500 RootObject* rootObject = obj->rootObject;
501 if (!rootObject || !rootObject->isValid())
502 return false;
503
504 ExecState* exec = rootObject->globalObject()->globalExec();
505 JSLock lock(SilenceAssertionsOnly);
506
507 // Call the constructor object.
508 JSValue constructor = obj->imp;
509 ConstructData constructData;
510 ConstructType constructType = getConstructData(constructor, constructData);
511 if (constructType == ConstructTypeNone)
512 return false;
513
514 MarkedArgumentBuffer argList;
515 getListFromVariantArgs(exec, args, argCount, rootObject, argList);
516 RefPtr<JSGlobalData> globalData(&exec->globalData());
517 globalData->timeoutChecker.start();
518 JSValue resultV = JSC::construct(exec, constructor, constructType, constructData, argList);
519 globalData->timeoutChecker.stop();
520
521 // Convert and return the result.
522 convertValueToNPVariant(exec, resultV, result);
523 exec->clearException();
524 return true;
525 }
526
527 if (NP_CLASS_STRUCT_VERSION_HAS_CTOR(o->_class) && o->_class->construct)
528 return o->_class->construct(o, args, argCount, result);
529
530 return false;
531 }
532
533 #endif // ENABLE(NETSCAPE_PLUGIN_API)
534