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: 56 NPObject* get(RootObject* rootObject, JSObject* jsObject) 57 { 58 return m_map.get(rootObject).get(jsObject); 59 } 60 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 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 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 101 static ObjectMap& objectMap() 102 { 103 DEFINE_STATIC_LOCAL(ObjectMap, map, ()); 104 return map; 105 } 106 107 void ObjectMap::RootObjectInvalidationCallback::operator()(RootObject* rootObject) 108 { 109 objectMap().remove(rootObject); 110 } 111 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 118 static NPObject* jsAllocate(NPP, NPClass*) 119 { 120 return static_cast<NPObject*>(malloc(sizeof(JavaScriptObject))); 121 } 122 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 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 163 NPObject* _NPN_CreateNoScriptObject(void) 164 { 165 return _NPN_CreateObject(0, NPNoScriptObjectClass); 166 } 167 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 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 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 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 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 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 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 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 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 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 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