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