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