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 "JSNPObject.h" 28 29#if ENABLE(NETSCAPE_PLUGIN_API) 30 31#include "JSNPMethod.h" 32#include "NPJSObject.h" 33#include "NPRuntimeObjectMap.h" 34#include "NPRuntimeUtilities.h" 35#include <JavaScriptCore/Error.h> 36#include <JavaScriptCore/JSGlobalObject.h> 37#include <JavaScriptCore/JSLock.h> 38#include <JavaScriptCore/ObjectPrototype.h> 39#include <WebCore/IdentifierRep.h> 40#include <WebCore/JSDOMWindowBase.h> 41#include <wtf/Assertions.h> 42#include <wtf/text/WTFString.h> 43 44using namespace JSC; 45using namespace WebCore; 46 47namespace WebKit { 48 49static NPIdentifier npIdentifierFromIdentifier(PropertyName propertyName) 50{ 51 String name(propertyName.publicName()); 52 if (name.isNull()) 53 return 0; 54 return static_cast<NPIdentifier>(IdentifierRep::get(name.utf8().data())); 55} 56 57const ClassInfo JSNPObject::s_info = { "NPObject", &Base::s_info, 0, 0, CREATE_METHOD_TABLE(JSNPObject) }; 58 59JSNPObject::JSNPObject(JSGlobalObject* globalObject, Structure* structure, NPRuntimeObjectMap* objectMap, NPObject* npObject) 60 : JSDestructibleObject(globalObject->vm(), structure) 61 , m_objectMap(objectMap) 62 , m_npObject(npObject) 63{ 64 ASSERT(globalObject == structure->globalObject()); 65} 66 67void JSNPObject::finishCreation(JSGlobalObject* globalObject) 68{ 69 Base::finishCreation(globalObject->vm()); 70 ASSERT(inherits(&s_info)); 71 72 // We should never have an NPJSObject inside a JSNPObject. 73 ASSERT(!NPJSObject::isNPJSObject(m_npObject)); 74 75 retainNPObject(m_npObject); 76} 77 78JSNPObject::~JSNPObject() 79{ 80 if (m_npObject) 81 invalidate(); 82} 83 84void JSNPObject::destroy(JSCell* cell) 85{ 86 static_cast<JSNPObject*>(cell)->JSNPObject::~JSNPObject(); 87} 88 89void JSNPObject::invalidate() 90{ 91 ASSERT(m_npObject); 92 ASSERT_GC_OBJECT_INHERITS(this, &s_info); 93 94 releaseNPObject(m_npObject); 95 m_npObject = 0; 96} 97 98NPObject* JSNPObject::leakNPObject() 99{ 100 ASSERT(m_npObject); 101 102 NPObject* object = m_npObject; 103 m_npObject = 0; 104 return object; 105} 106 107JSValue JSNPObject::callMethod(ExecState* exec, NPIdentifier methodName) 108{ 109 ASSERT_GC_OBJECT_INHERITS(this, &s_info); 110 if (!m_npObject) 111 return throwInvalidAccessError(exec); 112 113 size_t argumentCount = exec->argumentCount(); 114 Vector<NPVariant, 8> arguments(argumentCount); 115 116 // Convert all arguments to NPVariants. 117 for (size_t i = 0; i < argumentCount; ++i) 118 m_objectMap->convertJSValueToNPVariant(exec, exec->argument(i), arguments[i]); 119 120 // Calling NPClass::invoke will call into plug-in code, and there's no telling what the plug-in can do. 121 // (including destroying the plug-in). Because of this, we make sure to keep the plug-in alive until 122 // the call has finished. 123 NPRuntimeObjectMap::PluginProtector protector(m_objectMap); 124 125 bool returnValue; 126 NPVariant result; 127 VOID_TO_NPVARIANT(result); 128 129 { 130 JSLock::DropAllLocks dropAllLocks(JSDOMWindowBase::commonVM()); 131 returnValue = m_npObject->_class->invoke(m_npObject, methodName, arguments.data(), argumentCount, &result); 132 NPRuntimeObjectMap::moveGlobalExceptionToExecState(exec); 133 } 134 135 // Release all arguments; 136 for (size_t i = 0; i < argumentCount; ++i) 137 releaseNPVariantValue(&arguments[i]); 138 139 if (!returnValue) 140 throwError(exec, createError(exec, "Error calling method on NPObject.")); 141 142 JSValue propertyValue = m_objectMap->convertNPVariantToJSValue(exec, globalObject(), result); 143 releaseNPVariantValue(&result); 144 return propertyValue; 145} 146 147JSC::JSValue JSNPObject::callObject(JSC::ExecState* exec) 148{ 149 ASSERT_GC_OBJECT_INHERITS(this, &s_info); 150 if (!m_npObject) 151 return throwInvalidAccessError(exec); 152 153 size_t argumentCount = exec->argumentCount(); 154 Vector<NPVariant, 8> arguments(argumentCount); 155 156 // Convert all arguments to NPVariants. 157 for (size_t i = 0; i < argumentCount; ++i) 158 m_objectMap->convertJSValueToNPVariant(exec, exec->argument(i), arguments[i]); 159 160 // Calling NPClass::invokeDefault will call into plug-in code, and there's no telling what the plug-in can do. 161 // (including destroying the plug-in). Because of this, we make sure to keep the plug-in alive until 162 // the call has finished. 163 NPRuntimeObjectMap::PluginProtector protector(m_objectMap); 164 165 bool returnValue; 166 NPVariant result; 167 VOID_TO_NPVARIANT(result); 168 169 { 170 JSLock::DropAllLocks dropAllLocks(JSDOMWindowBase::commonVM()); 171 returnValue = m_npObject->_class->invokeDefault(m_npObject, arguments.data(), argumentCount, &result); 172 NPRuntimeObjectMap::moveGlobalExceptionToExecState(exec); 173 } 174 175 // Release all arguments; 176 for (size_t i = 0; i < argumentCount; ++i) 177 releaseNPVariantValue(&arguments[i]); 178 179 if (!returnValue) 180 throwError(exec, createError(exec, "Error calling method on NPObject.")); 181 182 JSValue propertyValue = m_objectMap->convertNPVariantToJSValue(exec, globalObject(), result); 183 releaseNPVariantValue(&result); 184 return propertyValue; 185} 186 187JSValue JSNPObject::callConstructor(ExecState* exec) 188{ 189 ASSERT_GC_OBJECT_INHERITS(this, &s_info); 190 if (!m_npObject) 191 return throwInvalidAccessError(exec); 192 193 size_t argumentCount = exec->argumentCount(); 194 Vector<NPVariant, 8> arguments(argumentCount); 195 196 // Convert all arguments to NPVariants. 197 for (size_t i = 0; i < argumentCount; ++i) 198 m_objectMap->convertJSValueToNPVariant(exec, exec->argument(i), arguments[i]); 199 200 // Calling NPClass::construct will call into plug-in code, and there's no telling what the plug-in can do. 201 // (including destroying the plug-in). Because of this, we make sure to keep the plug-in alive until 202 // the call has finished. 203 NPRuntimeObjectMap::PluginProtector protector(m_objectMap); 204 205 bool returnValue; 206 NPVariant result; 207 VOID_TO_NPVARIANT(result); 208 209 { 210 JSLock::DropAllLocks dropAllLocks(JSDOMWindowBase::commonVM()); 211 returnValue = m_npObject->_class->construct(m_npObject, arguments.data(), argumentCount, &result); 212 NPRuntimeObjectMap::moveGlobalExceptionToExecState(exec); 213 } 214 215 if (!returnValue) 216 throwError(exec, createError(exec, "Error calling method on NPObject.")); 217 218 JSValue value = m_objectMap->convertNPVariantToJSValue(exec, globalObject(), result); 219 releaseNPVariantValue(&result); 220 return value; 221} 222 223static EncodedJSValue JSC_HOST_CALL callNPJSObject(ExecState* exec) 224{ 225 JSObject* object = exec->callee(); 226 ASSERT(object->inherits(&JSNPObject::s_info)); 227 228 return JSValue::encode(static_cast<JSNPObject*>(object)->callObject(exec)); 229} 230 231JSC::CallType JSNPObject::getCallData(JSC::JSCell* cell, JSC::CallData& callData) 232{ 233 JSNPObject* thisObject = JSC::jsCast<JSNPObject*>(cell); 234 ASSERT_GC_OBJECT_INHERITS(thisObject, &s_info); 235 if (!thisObject->m_npObject || !thisObject->m_npObject->_class->invokeDefault) 236 return CallTypeNone; 237 238 callData.native.function = callNPJSObject; 239 return CallTypeHost; 240} 241 242static EncodedJSValue JSC_HOST_CALL constructWithConstructor(ExecState* exec) 243{ 244 JSObject* constructor = exec->callee(); 245 ASSERT(constructor->inherits(&JSNPObject::s_info)); 246 247 return JSValue::encode(static_cast<JSNPObject*>(constructor)->callConstructor(exec)); 248} 249 250ConstructType JSNPObject::getConstructData(JSCell* cell, ConstructData& constructData) 251{ 252 JSNPObject* thisObject = JSC::jsCast<JSNPObject*>(cell); 253 ASSERT_GC_OBJECT_INHERITS(thisObject, &s_info); 254 if (!thisObject->m_npObject || !thisObject->m_npObject->_class->construct) 255 return ConstructTypeNone; 256 257 constructData.native.function = constructWithConstructor; 258 return ConstructTypeHost; 259} 260 261bool JSNPObject::getOwnPropertySlot(JSCell* cell, ExecState* exec, PropertyName propertyName, PropertySlot& slot) 262{ 263 JSNPObject* thisObject = JSC::jsCast<JSNPObject*>(cell); 264 ASSERT_GC_OBJECT_INHERITS(thisObject, &s_info); 265 if (!thisObject->m_npObject) { 266 throwInvalidAccessError(exec); 267 return false; 268 } 269 270 NPIdentifier npIdentifier = npIdentifierFromIdentifier(propertyName); 271 272 // Calling NPClass::invoke will call into plug-in code, and there's no telling what the plug-in can do. 273 // (including destroying the plug-in). Because of this, we make sure to keep the plug-in alive until 274 // the call has finished. 275 NPRuntimeObjectMap::PluginProtector protector(thisObject->m_objectMap); 276 277 // First, check if the NPObject has a property with this name. 278 if (thisObject->m_npObject->_class->hasProperty && thisObject->m_npObject->_class->hasProperty(thisObject->m_npObject, npIdentifier)) { 279 slot.setCustom(thisObject, thisObject->propertyGetter); 280 return true; 281 } 282 283 // Second, check if the NPObject has a method with this name. 284 if (thisObject->m_npObject->_class->hasMethod && thisObject->m_npObject->_class->hasMethod(thisObject->m_npObject, npIdentifier)) { 285 slot.setCustom(thisObject, thisObject->methodGetter); 286 return true; 287 } 288 289 return false; 290} 291 292bool JSNPObject::getOwnPropertyDescriptor(JSObject* object, ExecState* exec, PropertyName propertyName, PropertyDescriptor& descriptor) 293{ 294 JSNPObject* thisObject = jsCast<JSNPObject*>(object); 295 ASSERT_GC_OBJECT_INHERITS(thisObject, &s_info); 296 if (!thisObject->m_npObject) { 297 throwInvalidAccessError(exec); 298 return false; 299 } 300 301 NPIdentifier npIdentifier = npIdentifierFromIdentifier(propertyName); 302 303 // Calling NPClass::invoke will call into plug-in code, and there's no telling what the plug-in can do. 304 // (including destroying the plug-in). Because of this, we make sure to keep the plug-in alive until 305 // the call has finished. 306 NPRuntimeObjectMap::PluginProtector protector(thisObject->m_objectMap); 307 308 // First, check if the NPObject has a property with this name. 309 if (thisObject->m_npObject->_class->hasProperty && thisObject->m_npObject->_class->hasProperty(thisObject->m_npObject, npIdentifier)) { 310 PropertySlot slot; 311 slot.setCustom(thisObject, propertyGetter); 312 descriptor.setDescriptor(slot.getValue(exec, propertyName), DontDelete); 313 return true; 314 } 315 316 // Second, check if the NPObject has a method with this name. 317 if (thisObject->m_npObject->_class->hasMethod && thisObject->m_npObject->_class->hasMethod(thisObject->m_npObject, npIdentifier)) { 318 PropertySlot slot; 319 slot.setCustom(thisObject, methodGetter); 320 descriptor.setDescriptor(slot.getValue(exec, propertyName), DontDelete | ReadOnly); 321 return true; 322 } 323 324 return false; 325} 326 327void JSNPObject::put(JSCell* cell, ExecState* exec, PropertyName propertyName, JSValue value, PutPropertySlot&) 328{ 329 JSNPObject* thisObject = JSC::jsCast<JSNPObject*>(cell); 330 ASSERT_GC_OBJECT_INHERITS(thisObject, &s_info); 331 if (!thisObject->m_npObject) { 332 throwInvalidAccessError(exec); 333 return; 334 } 335 336 NPIdentifier npIdentifier = npIdentifierFromIdentifier(propertyName); 337 338 if (!thisObject->m_npObject->_class->hasProperty || !thisObject->m_npObject->_class->hasProperty(thisObject->m_npObject, npIdentifier)) { 339 // FIXME: Should we throw an exception here? 340 return; 341 } 342 343 if (!thisObject->m_npObject->_class->setProperty) 344 return; 345 346 NPVariant variant; 347 thisObject->m_objectMap->convertJSValueToNPVariant(exec, value, variant); 348 349 // Calling NPClass::setProperty will call into plug-in code, and there's no telling what the plug-in can do. 350 // (including destroying the plug-in). Because of this, we make sure to keep the plug-in alive until 351 // the call has finished. 352 NPRuntimeObjectMap::PluginProtector protector(thisObject->m_objectMap); 353 354 { 355 JSLock::DropAllLocks dropAllLocks(JSDOMWindowBase::commonVM()); 356 thisObject->m_npObject->_class->setProperty(thisObject->m_npObject, npIdentifier, &variant); 357 358 NPRuntimeObjectMap::moveGlobalExceptionToExecState(exec); 359 360 // FIXME: Should we throw an exception if setProperty returns false? 361 } 362 363 releaseNPVariantValue(&variant); 364} 365 366bool JSNPObject::deleteProperty(JSCell* cell, ExecState* exec, PropertyName propertyName) 367{ 368 return jsCast<JSNPObject*>(cell)->deleteProperty(exec, npIdentifierFromIdentifier(propertyName)); 369} 370 371bool JSNPObject::deletePropertyByIndex(JSCell* cell, ExecState* exec, unsigned propertyName) 372{ 373 return jsCast<JSNPObject*>(cell)->deleteProperty(exec, static_cast<NPIdentifier>(IdentifierRep::get(propertyName))); 374} 375 376bool JSNPObject::deleteProperty(ExecState* exec, NPIdentifier propertyName) 377{ 378 ASSERT_GC_OBJECT_INHERITS(this, &s_info); 379 if (!m_npObject) { 380 throwInvalidAccessError(exec); 381 return false; 382 } 383 384 if (!m_npObject->_class->removeProperty) { 385 // FIXME: Should we throw an exception here? 386 return false; 387 } 388 389 // Calling NPClass::setProperty will call into plug-in code, and there's no telling what the plug-in can do. 390 // (including destroying the plug-in). Because of this, we make sure to keep the plug-in alive until 391 // the call has finished. 392 NPRuntimeObjectMap::PluginProtector protector(m_objectMap); 393 394 { 395 JSLock::DropAllLocks dropAllLocks(JSDOMWindowBase::commonVM()); 396 397 // FIXME: Should we throw an exception if removeProperty returns false? 398 if (!m_npObject->_class->removeProperty(m_npObject, propertyName)) 399 return false; 400 401 NPRuntimeObjectMap::moveGlobalExceptionToExecState(exec); 402 } 403 404 return true; 405} 406 407void JSNPObject::getOwnPropertyNames(JSObject* object, ExecState* exec, PropertyNameArray& propertyNameArray, EnumerationMode) 408{ 409 JSNPObject* thisObject = jsCast<JSNPObject*>(object); 410 ASSERT_GC_OBJECT_INHERITS(thisObject, &s_info); 411 if (!thisObject->m_npObject) { 412 throwInvalidAccessError(exec); 413 return; 414 } 415 416 if (!NP_CLASS_STRUCT_VERSION_HAS_ENUM(thisObject->m_npObject->_class) || !thisObject->m_npObject->_class->enumerate) 417 return; 418 419 NPIdentifier* identifiers = 0; 420 uint32_t identifierCount = 0; 421 422 // Calling NPClass::enumerate will call into plug-in code, and there's no telling what the plug-in can do. 423 // (including destroying the plug-in). Because of this, we make sure to keep the plug-in alive until 424 // the call has finished. 425 NPRuntimeObjectMap::PluginProtector protector(thisObject->m_objectMap); 426 427 { 428 JSLock::DropAllLocks dropAllLocks(JSDOMWindowBase::commonVM()); 429 430 // FIXME: Should we throw an exception if enumerate returns false? 431 if (!thisObject->m_npObject->_class->enumerate(thisObject->m_npObject, &identifiers, &identifierCount)) 432 return; 433 434 NPRuntimeObjectMap::moveGlobalExceptionToExecState(exec); 435 } 436 437 for (uint32_t i = 0; i < identifierCount; ++i) { 438 IdentifierRep* identifierRep = static_cast<IdentifierRep*>(identifiers[i]); 439 440 Identifier identifier; 441 if (identifierRep->isString()) { 442 const char* string = identifierRep->string(); 443 int length = strlen(string); 444 445 identifier = Identifier(exec, String::fromUTF8WithLatin1Fallback(string, length).impl()); 446 } else 447 identifier = Identifier::from(exec, identifierRep->number()); 448 449 propertyNameArray.add(identifier); 450 } 451 452 npnMemFree(identifiers); 453} 454 455JSValue JSNPObject::propertyGetter(ExecState* exec, JSValue slotBase, PropertyName propertyName) 456{ 457 JSNPObject* thisObj = static_cast<JSNPObject*>(asObject(slotBase)); 458 ASSERT_GC_OBJECT_INHERITS(thisObj, &s_info); 459 460 if (!thisObj->m_npObject) 461 return throwInvalidAccessError(exec); 462 463 if (!thisObj->m_npObject->_class->getProperty) 464 return jsUndefined(); 465 466 NPVariant result; 467 VOID_TO_NPVARIANT(result); 468 469 // Calling NPClass::getProperty will call into plug-in code, and there's no telling what the plug-in can do. 470 // (including destroying the plug-in). Because of this, we make sure to keep the plug-in alive until 471 // the call has finished. 472 NPRuntimeObjectMap::PluginProtector protector(thisObj->m_objectMap); 473 474 bool returnValue; 475 { 476 JSLock::DropAllLocks dropAllLocks(JSDOMWindowBase::commonVM()); 477 NPIdentifier npIdentifier = npIdentifierFromIdentifier(propertyName); 478 returnValue = thisObj->m_npObject->_class->getProperty(thisObj->m_npObject, npIdentifier, &result); 479 480 NPRuntimeObjectMap::moveGlobalExceptionToExecState(exec); 481 } 482 483 if (!returnValue) 484 return jsUndefined(); 485 486 JSValue propertyValue = thisObj->m_objectMap->convertNPVariantToJSValue(exec, thisObj->globalObject(), result); 487 releaseNPVariantValue(&result); 488 return propertyValue; 489} 490 491JSValue JSNPObject::methodGetter(ExecState* exec, JSValue slotBase, PropertyName propertyName) 492{ 493 JSNPObject* thisObj = static_cast<JSNPObject*>(asObject(slotBase)); 494 ASSERT_GC_OBJECT_INHERITS(thisObj, &s_info); 495 496 if (!thisObj->m_npObject) 497 return throwInvalidAccessError(exec); 498 499 NPIdentifier npIdentifier = npIdentifierFromIdentifier(propertyName); 500 return JSNPMethod::create(exec, thisObj->globalObject(), propertyName.publicName(), npIdentifier); 501} 502 503JSObject* JSNPObject::throwInvalidAccessError(ExecState* exec) 504{ 505 return throwError(exec, createReferenceError(exec, "Trying to access object from destroyed plug-in.")); 506} 507 508} // namespace WebKit 509 510#endif // ENABLE(NETSCAPE_PLUGIN_API) 511