1 /* 2 * Copyright (C) 2006, 2007, 2008, 2009 Google 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 are 6 * met: 7 * 8 * * Redistributions of source code must retain the above copyright 9 * notice, this list of conditions and the following disclaimer. 10 * * Redistributions in binary form must reproduce the above 11 * copyright notice, this list of conditions and the following disclaimer 12 * in the documentation and/or other materials provided with the 13 * distribution. 14 * * Neither the name of Google Inc. nor the names of its 15 * contributors may be used to endorse or promote products derived from 16 * this software without specific prior written permission. 17 * 18 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 19 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 20 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 21 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 22 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 23 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 24 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 25 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 26 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 27 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 28 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 29 */ 30 31 #include "config.h" 32 33 #include "bindings/core/v8/V8NPObject.h" 34 35 #include "bindings/core/v8/NPV8Object.h" 36 #include "bindings/core/v8/V8Binding.h" 37 #include "bindings/core/v8/V8HTMLAppletElement.h" 38 #include "bindings/core/v8/V8HTMLEmbedElement.h" 39 #include "bindings/core/v8/V8HTMLObjectElement.h" 40 #include "bindings/core/v8/V8NPUtils.h" 41 #include "bindings/core/v8/V8ObjectConstructor.h" 42 #include "bindings/core/v8/V8PersistentValueMap.h" 43 #include "bindings/core/v8/npruntime_impl.h" 44 #include "bindings/core/v8/npruntime_priv.h" 45 #include "core/html/HTMLPlugInElement.h" 46 #include "v8-util.h" 47 #include "wtf/OwnPtr.h" 48 49 namespace blink { 50 51 enum InvokeFunctionType { 52 InvokeMethod = 1, 53 InvokeConstruct = 2, 54 InvokeDefault = 3 55 }; 56 57 struct IdentifierRep { 58 int number() const { return m_isString ? 0 : m_value.m_number; } 59 const char* string() const { return m_isString ? m_value.m_string : 0; } 60 61 union { 62 const char* m_string; 63 int m_number; 64 } m_value; 65 bool m_isString; 66 }; 67 68 // FIXME: need comments. 69 // Params: holder could be HTMLEmbedElement or NPObject 70 static void npObjectInvokeImpl(const v8::FunctionCallbackInfo<v8::Value>& info, InvokeFunctionType functionId) 71 { 72 NPObject* npObject; 73 v8::Isolate* isolate = info.GetIsolate(); 74 75 // These three types are subtypes of HTMLPlugInElement. 76 HTMLPlugInElement* element = V8HTMLAppletElement::toImplWithTypeCheck(isolate, info.Holder()); 77 if (!element) { 78 element = V8HTMLEmbedElement::toImplWithTypeCheck(isolate, info.Holder()); 79 if (!element) { 80 element = V8HTMLObjectElement::toImplWithTypeCheck(isolate, info.Holder()); 81 } 82 } 83 if (element) { 84 if (RefPtr<SharedPersistent<v8::Object> > wrapper = element->pluginWrapper()) { 85 v8::HandleScope handleScope(isolate); 86 npObject = v8ObjectToNPObject(wrapper->newLocal(isolate)); 87 } else { 88 npObject = 0; 89 } 90 } else { 91 // The holder object is not a subtype of HTMLPlugInElement, it must be an NPObject which has three 92 // internal fields. 93 if (info.Holder()->InternalFieldCount() != npObjectInternalFieldCount) { 94 V8ThrowException::throwReferenceError("NPMethod called on non-NPObject", info.GetIsolate()); 95 return; 96 } 97 98 npObject = v8ObjectToNPObject(info.Holder()); 99 } 100 101 // Verify that our wrapper wasn't using a NPObject which has already been deleted. 102 if (!npObject || !_NPN_IsAlive(npObject)) { 103 V8ThrowException::throwReferenceError("NPObject deleted", isolate); 104 return; 105 } 106 107 // Wrap up parameters. 108 int numArgs = info.Length(); 109 OwnPtr<NPVariant[]> npArgs = adoptArrayPtr(new NPVariant[numArgs]); 110 111 for (int i = 0; i < numArgs; i++) 112 convertV8ObjectToNPVariant(info[i], npObject, &npArgs[i], isolate); 113 114 NPVariant result; 115 VOID_TO_NPVARIANT(result); 116 117 bool retval = true; 118 switch (functionId) { 119 case InvokeMethod: 120 if (npObject->_class->invoke) { 121 v8::Handle<v8::String> functionName = v8::Handle<v8::String>::Cast(info.Data()); 122 NPIdentifier identifier = getStringIdentifier(functionName); 123 retval = npObject->_class->invoke(npObject, identifier, npArgs.get(), numArgs, &result); 124 } 125 break; 126 case InvokeConstruct: 127 if (npObject->_class->construct) 128 retval = npObject->_class->construct(npObject, npArgs.get(), numArgs, &result); 129 break; 130 case InvokeDefault: 131 if (npObject->_class->invokeDefault) 132 retval = npObject->_class->invokeDefault(npObject, npArgs.get(), numArgs, &result); 133 break; 134 default: 135 break; 136 } 137 138 if (!retval) 139 V8ThrowException::throwGeneralError("Error calling method on NPObject.", isolate); 140 141 for (int i = 0; i < numArgs; i++) 142 _NPN_ReleaseVariantValue(&npArgs[i]); 143 144 // Unwrap return values. 145 v8::Handle<v8::Value> returnValue; 146 if (_NPN_IsAlive(npObject)) 147 returnValue = convertNPVariantToV8Object(&result, npObject, isolate); 148 _NPN_ReleaseVariantValue(&result); 149 150 v8SetReturnValue(info, returnValue); 151 } 152 153 154 void npObjectMethodHandler(const v8::FunctionCallbackInfo<v8::Value>& info) 155 { 156 return npObjectInvokeImpl(info, InvokeMethod); 157 } 158 159 160 void npObjectInvokeDefaultHandler(const v8::FunctionCallbackInfo<v8::Value>& info) 161 { 162 if (info.IsConstructCall()) { 163 npObjectInvokeImpl(info, InvokeConstruct); 164 return; 165 } 166 167 npObjectInvokeImpl(info, InvokeDefault); 168 } 169 170 class V8TemplateMapTraits : public V8PersistentValueMapTraits<PrivateIdentifier*, v8::FunctionTemplate, true> { 171 public: 172 typedef v8::PersistentValueMap<PrivateIdentifier*, v8::FunctionTemplate, V8TemplateMapTraits> MapType; 173 typedef PrivateIdentifier WeakCallbackDataType; 174 175 static WeakCallbackDataType* WeakCallbackParameter(MapType* map, PrivateIdentifier* key, const v8::Local<v8::FunctionTemplate>& value) 176 { 177 return key; 178 } 179 180 static void DisposeCallbackData(WeakCallbackDataType* callbackData) { } 181 182 static MapType* MapFromWeakCallbackData( 183 const v8::WeakCallbackData<v8::FunctionTemplate, WeakCallbackDataType>&); 184 185 static PrivateIdentifier* KeyFromWeakCallbackData( 186 const v8::WeakCallbackData<v8::FunctionTemplate, WeakCallbackDataType>& data) 187 { 188 return data.GetParameter(); 189 } 190 191 // Dispose traits: 192 static void Dispose(v8::Isolate* isolate, v8::UniquePersistent<v8::FunctionTemplate> value, PrivateIdentifier* key) { } 193 }; 194 195 196 class V8NPTemplateMap { 197 public: 198 // NPIdentifier is PrivateIdentifier*. 199 typedef v8::PersistentValueMap<PrivateIdentifier*, v8::FunctionTemplate, V8TemplateMapTraits> MapType; 200 201 v8::Local<v8::FunctionTemplate> get(PrivateIdentifier* key) 202 { 203 return m_map.Get(key); 204 } 205 206 void set(PrivateIdentifier* key, v8::Handle<v8::FunctionTemplate> handle) 207 { 208 ASSERT(!m_map.Contains(key)); 209 m_map.Set(key, handle); 210 } 211 212 static V8NPTemplateMap& sharedInstance(v8::Isolate* isolate) 213 { 214 DEFINE_STATIC_LOCAL(V8NPTemplateMap, map, (isolate)); 215 ASSERT(isolate == map.m_map.GetIsolate()); 216 return map; 217 } 218 219 friend class V8TemplateMapTraits; 220 221 private: 222 explicit V8NPTemplateMap(v8::Isolate* isolate) 223 : m_map(isolate) 224 { 225 } 226 227 MapType m_map; 228 }; 229 230 V8TemplateMapTraits::MapType* V8TemplateMapTraits::MapFromWeakCallbackData(const v8::WeakCallbackData<v8::FunctionTemplate, WeakCallbackDataType>& data) 231 { 232 return &V8NPTemplateMap::sharedInstance(data.GetIsolate()).m_map; 233 } 234 235 236 static v8::Handle<v8::Value> npObjectGetProperty(v8::Local<v8::Object> self, NPIdentifier identifier, v8::Local<v8::Value> key, v8::Isolate* isolate) 237 { 238 NPObject* npObject = v8ObjectToNPObject(self); 239 240 // Verify that our wrapper wasn't using a NPObject which 241 // has already been deleted. 242 if (!npObject || !_NPN_IsAlive(npObject)) 243 return V8ThrowException::throwReferenceError("NPObject deleted", isolate); 244 245 246 if (npObject->_class->hasProperty && npObject->_class->getProperty && npObject->_class->hasProperty(npObject, identifier)) { 247 if (!_NPN_IsAlive(npObject)) 248 return V8ThrowException::throwReferenceError("NPObject deleted", isolate); 249 250 NPVariant result; 251 VOID_TO_NPVARIANT(result); 252 if (!npObject->_class->getProperty(npObject, identifier, &result)) 253 return v8Undefined(); 254 255 v8::Handle<v8::Value> returnValue; 256 if (_NPN_IsAlive(npObject)) 257 returnValue = convertNPVariantToV8Object(&result, npObject, isolate); 258 _NPN_ReleaseVariantValue(&result); 259 return returnValue; 260 261 } 262 263 if (!_NPN_IsAlive(npObject)) 264 return V8ThrowException::throwReferenceError("NPObject deleted", isolate); 265 266 if (key->IsString() && npObject->_class->hasMethod && npObject->_class->hasMethod(npObject, identifier)) { 267 if (!_NPN_IsAlive(npObject)) 268 return V8ThrowException::throwReferenceError("NPObject deleted", isolate); 269 270 PrivateIdentifier* id = static_cast<PrivateIdentifier*>(identifier); 271 v8::Local<v8::FunctionTemplate> functionTemplate = V8NPTemplateMap::sharedInstance(isolate).get(id); 272 // Cache templates using identifier as the key. 273 if (functionTemplate.IsEmpty()) { 274 // Create a new template. 275 functionTemplate = v8::FunctionTemplate::New(isolate); 276 functionTemplate->SetCallHandler(npObjectMethodHandler, key); 277 V8NPTemplateMap::sharedInstance(isolate).set(id, functionTemplate); 278 } 279 v8::Local<v8::Function> v8Function = functionTemplate->GetFunction(); 280 v8Function->SetName(v8::Handle<v8::String>::Cast(key)); 281 return v8Function; 282 } 283 284 return v8Undefined(); 285 } 286 287 void npObjectNamedPropertyGetter(v8::Local<v8::String> name, const v8::PropertyCallbackInfo<v8::Value>& info) 288 { 289 NPIdentifier identifier = getStringIdentifier(name); 290 v8SetReturnValue(info, npObjectGetProperty(info.Holder(), identifier, name, info.GetIsolate())); 291 } 292 293 void npObjectIndexedPropertyGetter(uint32_t index, const v8::PropertyCallbackInfo<v8::Value>& info) 294 { 295 NPIdentifier identifier = _NPN_GetIntIdentifier(index); 296 v8SetReturnValue(info, npObjectGetProperty(info.Holder(), identifier, v8::Number::New(info.GetIsolate(), index), info.GetIsolate())); 297 } 298 299 void npObjectGetNamedProperty(v8::Local<v8::Object> self, v8::Local<v8::String> name, const v8::PropertyCallbackInfo<v8::Value>& info) 300 { 301 NPIdentifier identifier = getStringIdentifier(name); 302 v8SetReturnValue(info, npObjectGetProperty(self, identifier, name, info.GetIsolate())); 303 } 304 305 void npObjectGetIndexedProperty(v8::Local<v8::Object> self, uint32_t index, const v8::PropertyCallbackInfo<v8::Value>& info) 306 { 307 NPIdentifier identifier = _NPN_GetIntIdentifier(index); 308 v8SetReturnValue(info, npObjectGetProperty(self, identifier, v8::Number::New(info.GetIsolate(), index), info.GetIsolate())); 309 } 310 311 void npObjectQueryProperty(v8::Local<v8::String> name, const v8::PropertyCallbackInfo<v8::Integer>& info) 312 { 313 NPIdentifier identifier = getStringIdentifier(name); 314 if (npObjectGetProperty(info.Holder(), identifier, name, info.GetIsolate()).IsEmpty()) 315 return; 316 v8SetReturnValueInt(info, 0); 317 } 318 319 static v8::Handle<v8::Value> npObjectSetProperty(v8::Local<v8::Object> self, NPIdentifier identifier, v8::Local<v8::Value> value, v8::Isolate* isolate) 320 { 321 NPObject* npObject = v8ObjectToNPObject(self); 322 323 // Verify that our wrapper wasn't using a NPObject which has already been deleted. 324 if (!npObject || !_NPN_IsAlive(npObject)) { 325 V8ThrowException::throwReferenceError("NPObject deleted", isolate); 326 return value; // Intercepted, but an exception was thrown. 327 } 328 329 if (npObject->_class->hasProperty && npObject->_class->setProperty && npObject->_class->hasProperty(npObject, identifier)) { 330 if (!_NPN_IsAlive(npObject)) 331 return V8ThrowException::throwReferenceError("NPObject deleted", isolate); 332 333 NPVariant npValue; 334 VOID_TO_NPVARIANT(npValue); 335 convertV8ObjectToNPVariant(value, npObject, &npValue, isolate); 336 bool success = npObject->_class->setProperty(npObject, identifier, &npValue); 337 _NPN_ReleaseVariantValue(&npValue); 338 if (success) 339 return value; // Intercept the call. 340 } 341 return v8Undefined(); 342 } 343 344 345 void npObjectNamedPropertySetter(v8::Local<v8::String> name, v8::Local<v8::Value> value, const v8::PropertyCallbackInfo<v8::Value>& info) 346 { 347 NPIdentifier identifier = getStringIdentifier(name); 348 v8SetReturnValue(info, npObjectSetProperty(info.Holder(), identifier, value, info.GetIsolate())); 349 } 350 351 352 void npObjectIndexedPropertySetter(uint32_t index, v8::Local<v8::Value> value, const v8::PropertyCallbackInfo<v8::Value>& info) 353 { 354 NPIdentifier identifier = _NPN_GetIntIdentifier(index); 355 v8SetReturnValue(info, npObjectSetProperty(info.Holder(), identifier, value, info.GetIsolate())); 356 } 357 358 void npObjectSetNamedProperty(v8::Local<v8::Object> self, v8::Local<v8::String> name, v8::Local<v8::Value> value, const v8::PropertyCallbackInfo<v8::Value>& info) 359 { 360 NPIdentifier identifier = getStringIdentifier(name); 361 v8SetReturnValue(info, npObjectSetProperty(self, identifier, value, info.GetIsolate())); 362 } 363 364 void npObjectSetIndexedProperty(v8::Local<v8::Object> self, uint32_t index, v8::Local<v8::Value> value, const v8::PropertyCallbackInfo<v8::Value>& info) 365 { 366 NPIdentifier identifier = _NPN_GetIntIdentifier(index); 367 v8SetReturnValue(info, npObjectSetProperty(self, identifier, value, info.GetIsolate())); 368 } 369 370 void npObjectPropertyEnumerator(const v8::PropertyCallbackInfo<v8::Array>& info, bool namedProperty) 371 { 372 NPObject* npObject = v8ObjectToNPObject(info.Holder()); 373 374 // Verify that our wrapper wasn't using a NPObject which 375 // has already been deleted. 376 if (!npObject || !_NPN_IsAlive(npObject)) { 377 V8ThrowException::throwReferenceError("NPObject deleted", info.GetIsolate()); 378 return; 379 } 380 381 if (NP_CLASS_STRUCT_VERSION_HAS_ENUM(npObject->_class) && npObject->_class->enumerate) { 382 uint32_t count; 383 NPIdentifier* identifiers; 384 if (npObject->_class->enumerate(npObject, &identifiers, &count)) { 385 uint32_t propertiesCount = 0; 386 for (uint32_t i = 0; i < count; ++i) { 387 IdentifierRep* identifier = static_cast<IdentifierRep*>(identifiers[i]); 388 if (namedProperty == identifier->m_isString) 389 ++propertiesCount; 390 } 391 v8::Handle<v8::Array> properties = v8::Array::New(info.GetIsolate(), propertiesCount); 392 for (uint32_t i = 0, propertyIndex = 0; i < count; ++i) { 393 IdentifierRep* identifier = static_cast<IdentifierRep*>(identifiers[i]); 394 if (namedProperty == identifier->m_isString) { 395 ASSERT(propertyIndex < propertiesCount); 396 if (namedProperty) 397 properties->Set(v8::Integer::New(info.GetIsolate(), propertyIndex++), v8AtomicString(info.GetIsolate(), identifier->string())); 398 else 399 properties->Set(v8::Integer::New(info.GetIsolate(), propertyIndex++), v8::Integer::New(info.GetIsolate(), identifier->number())); 400 } 401 } 402 403 v8SetReturnValue(info, properties); 404 return; 405 } 406 } 407 } 408 409 void npObjectNamedPropertyEnumerator(const v8::PropertyCallbackInfo<v8::Array>& info) 410 { 411 npObjectPropertyEnumerator(info, true); 412 } 413 414 void npObjectIndexedPropertyEnumerator(const v8::PropertyCallbackInfo<v8::Array>& info) 415 { 416 npObjectPropertyEnumerator(info, false); 417 } 418 419 static DOMWrapperMap<NPObject>& staticNPObjectMap() 420 { 421 DEFINE_STATIC_LOCAL(DOMWrapperMap<NPObject>, npObjectMap, (v8::Isolate::GetCurrent())); 422 return npObjectMap; 423 } 424 425 template <> 426 inline void DOMWrapperMap<NPObject>::PersistentValueMapTraits::Dispose( 427 v8::Isolate* isolate, 428 v8::UniquePersistent<v8::Object> value, 429 NPObject* npObject) 430 { 431 ASSERT(npObject); 432 if (_NPN_IsAlive(npObject)) 433 _NPN_ReleaseObject(npObject); 434 } 435 436 v8::Local<v8::Object> createV8ObjectForNPObject(NPObject* object, NPObject* root, v8::Isolate* isolate) 437 { 438 static v8::Eternal<v8::FunctionTemplate> npObjectDesc; 439 440 ASSERT(isolate->InContext()); 441 442 // If this is a v8 object, just return it. 443 V8NPObject* v8NPObject = npObjectToV8NPObject(object); 444 if (v8NPObject) 445 return v8::Local<v8::Object>::New(isolate, v8NPObject->v8Object); 446 447 // If we've already wrapped this object, just return it. 448 v8::Handle<v8::Object> wrapper = staticNPObjectMap().newLocal(object, isolate); 449 if (!wrapper.IsEmpty()) 450 return wrapper; 451 452 // FIXME: we should create a Wrapper type as a subclass of JSObject. It has two internal fields, field 0 is the wrapped 453 // pointer, and field 1 is the type. There should be an api function that returns unused type id. The same Wrapper type 454 // can be used by DOM bindings. 455 if (npObjectDesc.IsEmpty()) { 456 v8::Local<v8::FunctionTemplate> templ = v8::FunctionTemplate::New(isolate); 457 templ->InstanceTemplate()->SetInternalFieldCount(npObjectInternalFieldCount); 458 templ->InstanceTemplate()->SetNamedPropertyHandler(npObjectNamedPropertyGetter, npObjectNamedPropertySetter, npObjectQueryProperty, 0, npObjectNamedPropertyEnumerator); 459 templ->InstanceTemplate()->SetIndexedPropertyHandler(npObjectIndexedPropertyGetter, npObjectIndexedPropertySetter, 0, 0, npObjectIndexedPropertyEnumerator); 460 templ->InstanceTemplate()->SetCallAsFunctionHandler(npObjectInvokeDefaultHandler); 461 npObjectDesc.Set(isolate, templ); 462 } 463 464 // FIXME: Move staticNPObjectMap() to DOMDataStore. 465 // Use V8DOMWrapper::createWrapper() and 466 // V8DOMWrapper::associateObjectWithWrapper() 467 // to create a wrapper object. 468 v8::Handle<v8::Function> v8Function = npObjectDesc.Get(isolate)->GetFunction(); 469 v8::Local<v8::Object> value = V8ObjectConstructor::newInstance(isolate, v8Function); 470 if (value.IsEmpty()) 471 return value; 472 473 V8DOMWrapper::setNativeInfo(value, npObjectTypeInfo(), npObjectToScriptWrappableBase(object)); 474 475 // KJS retains the object as part of its wrapper (see Bindings::CInstance). 476 _NPN_RetainObject(object); 477 _NPN_RegisterObject(object, root); 478 479 staticNPObjectMap().set(object, value, npObjectTypeInfo()); 480 ASSERT(V8DOMWrapper::isDOMWrapper(value)); 481 return value; 482 } 483 484 void forgetV8ObjectForNPObject(NPObject* object) 485 { 486 v8::Isolate* isolate = v8::Isolate::GetCurrent(); 487 v8::HandleScope scope(isolate); 488 v8::Handle<v8::Object> wrapper = staticNPObjectMap().newLocal(object, isolate); 489 if (!wrapper.IsEmpty()) { 490 V8DOMWrapper::clearNativeInfo(wrapper, npObjectTypeInfo()); 491 staticNPObjectMap().removeAndDispose(object); 492 _NPN_ReleaseObject(object); 493 } 494 } 495 496 } // namespace blink 497