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/v8/V8NPObject.h" 34 35 #include "V8HTMLAppletElement.h" 36 #include "V8HTMLEmbedElement.h" 37 #include "V8HTMLObjectElement.h" 38 #include "bindings/v8/NPV8Object.h" 39 #include "bindings/v8/UnsafePersistent.h" 40 #include "bindings/v8/V8Binding.h" 41 #include "bindings/v8/V8NPUtils.h" 42 #include "bindings/v8/V8ObjectConstructor.h" 43 #include "bindings/v8/npruntime_impl.h" 44 #include "bindings/v8/npruntime_priv.h" 45 #include "core/html/HTMLPlugInElement.h" 46 #include "wtf/OwnArrayPtr.h" 47 48 namespace WebCore { 49 50 enum InvokeFunctionType { 51 InvokeMethod = 1, 52 InvokeConstruct = 2, 53 InvokeDefault = 3 54 }; 55 56 struct IdentifierRep { 57 int number() const { return m_isString ? 0 : m_value.m_number; } 58 const char* string() const { return m_isString ? m_value.m_string : 0; } 59 60 union { 61 const char* m_string; 62 int m_number; 63 } m_value; 64 bool m_isString; 65 }; 66 67 // FIXME: need comments. 68 // Params: holder could be HTMLEmbedElement or NPObject 69 static void npObjectInvokeImpl(const v8::FunctionCallbackInfo<v8::Value>& args, InvokeFunctionType functionId) 70 { 71 NPObject* npObject; 72 73 WrapperWorldType currentWorldType = worldType(args.GetIsolate()); 74 // These three types are subtypes of HTMLPlugInElement. 75 if (V8HTMLAppletElement::HasInstance(args.Holder(), args.GetIsolate(), currentWorldType) || V8HTMLEmbedElement::HasInstance(args.Holder(), args.GetIsolate(), currentWorldType) 76 || V8HTMLObjectElement::HasInstance(args.Holder(), args.GetIsolate(), currentWorldType)) { 77 // The holder object is a subtype of HTMLPlugInElement. 78 HTMLPlugInElement* element; 79 if (V8HTMLAppletElement::HasInstance(args.Holder(), args.GetIsolate(), currentWorldType)) 80 element = V8HTMLAppletElement::toNative(args.Holder()); 81 else if (V8HTMLEmbedElement::HasInstance(args.Holder(), args.GetIsolate(), currentWorldType)) 82 element = V8HTMLEmbedElement::toNative(args.Holder()); 83 else 84 element = V8HTMLObjectElement::toNative(args.Holder()); 85 ScriptInstance scriptInstance = element->getInstance(); 86 if (scriptInstance) { 87 v8::Isolate* isolate = v8::Isolate::GetCurrent(); 88 v8::HandleScope handleScope(isolate); 89 npObject = v8ObjectToNPObject(scriptInstance->newLocal(isolate)); 90 } else 91 npObject = 0; 92 } else { 93 // The holder object is not a subtype of HTMLPlugInElement, it must be an NPObject which has three 94 // internal fields. 95 if (args.Holder()->InternalFieldCount() != npObjectInternalFieldCount) { 96 throwError(v8ReferenceError, "NPMethod called on non-NPObject", args.GetIsolate()); 97 return; 98 } 99 100 npObject = v8ObjectToNPObject(args.Holder()); 101 } 102 103 // Verify that our wrapper wasn't using a NPObject which has already been deleted. 104 if (!npObject || !_NPN_IsAlive(npObject)) { 105 throwError(v8ReferenceError, "NPObject deleted", args.GetIsolate()); 106 return; 107 } 108 109 // Wrap up parameters. 110 int numArgs = args.Length(); 111 OwnArrayPtr<NPVariant> npArgs = adoptArrayPtr(new NPVariant[numArgs]); 112 113 for (int i = 0; i < numArgs; i++) 114 convertV8ObjectToNPVariant(args[i], npObject, &npArgs[i]); 115 116 NPVariant result; 117 VOID_TO_NPVARIANT(result); 118 119 bool retval = true; 120 switch (functionId) { 121 case InvokeMethod: 122 if (npObject->_class->invoke) { 123 v8::Handle<v8::String> functionName = v8::Handle<v8::String>::Cast(args.Data()); 124 NPIdentifier identifier = getStringIdentifier(functionName); 125 retval = npObject->_class->invoke(npObject, identifier, npArgs.get(), numArgs, &result); 126 } 127 break; 128 case InvokeConstruct: 129 if (npObject->_class->construct) 130 retval = npObject->_class->construct(npObject, npArgs.get(), numArgs, &result); 131 break; 132 case InvokeDefault: 133 if (npObject->_class->invokeDefault) 134 retval = npObject->_class->invokeDefault(npObject, npArgs.get(), numArgs, &result); 135 break; 136 default: 137 break; 138 } 139 140 if (!retval) 141 throwError(v8GeneralError, "Error calling method on NPObject.", args.GetIsolate()); 142 143 for (int i = 0; i < numArgs; i++) 144 _NPN_ReleaseVariantValue(&npArgs[i]); 145 146 // Unwrap return values. 147 v8::Handle<v8::Value> returnValue; 148 if (_NPN_IsAlive(npObject)) 149 returnValue = convertNPVariantToV8Object(&result, npObject, args.GetIsolate()); 150 _NPN_ReleaseVariantValue(&result); 151 152 v8SetReturnValue(args, returnValue); 153 } 154 155 156 void npObjectMethodHandler(const v8::FunctionCallbackInfo<v8::Value>& args) 157 { 158 return npObjectInvokeImpl(args, InvokeMethod); 159 } 160 161 162 void npObjectInvokeDefaultHandler(const v8::FunctionCallbackInfo<v8::Value>& args) 163 { 164 if (args.IsConstructCall()) { 165 npObjectInvokeImpl(args, InvokeConstruct); 166 return; 167 } 168 169 npObjectInvokeImpl(args, InvokeDefault); 170 } 171 172 class V8NPTemplateMap { 173 public: 174 // NPIdentifier is PrivateIdentifier*. 175 typedef HashMap<PrivateIdentifier*, UnsafePersistent<v8::FunctionTemplate> > MapType; 176 177 UnsafePersistent<v8::FunctionTemplate> get(PrivateIdentifier* key) 178 { 179 return m_map.get(key); 180 } 181 182 void set(PrivateIdentifier* key, v8::Handle<v8::FunctionTemplate> handle) 183 { 184 ASSERT(!m_map.contains(key)); 185 v8::Persistent<v8::FunctionTemplate> wrapper(m_isolate, handle); 186 wrapper.MakeWeak(key, &makeWeakCallback); 187 m_map.set(key, UnsafePersistent<v8::FunctionTemplate>(wrapper)); 188 } 189 190 static V8NPTemplateMap& sharedInstance(v8::Isolate* isolate) 191 { 192 DEFINE_STATIC_LOCAL(V8NPTemplateMap, map, (isolate)); 193 ASSERT(isolate == map.m_isolate); 194 return map; 195 } 196 197 private: 198 explicit V8NPTemplateMap(v8::Isolate* isolate) 199 : m_isolate(isolate) 200 { 201 } 202 203 void dispose(PrivateIdentifier* key) 204 { 205 MapType::iterator it = m_map.find(key); 206 ASSERT(it != m_map.end()); 207 it->value.dispose(); 208 m_map.remove(it); 209 } 210 211 static void makeWeakCallback(v8::Isolate* isolate, v8::Persistent<v8::FunctionTemplate>*, PrivateIdentifier* key) 212 { 213 V8NPTemplateMap::sharedInstance(isolate).dispose(key); 214 } 215 216 MapType m_map; 217 v8::Isolate* m_isolate; 218 }; 219 220 static v8::Handle<v8::Value> npObjectGetProperty(v8::Local<v8::Object> self, NPIdentifier identifier, v8::Local<v8::Value> key, v8::Isolate* isolate) 221 { 222 NPObject* npObject = v8ObjectToNPObject(self); 223 224 // Verify that our wrapper wasn't using a NPObject which 225 // has already been deleted. 226 if (!npObject || !_NPN_IsAlive(npObject)) 227 return throwError(v8ReferenceError, "NPObject deleted", isolate); 228 229 230 if (npObject->_class->hasProperty && npObject->_class->getProperty && npObject->_class->hasProperty(npObject, identifier)) { 231 if (!_NPN_IsAlive(npObject)) 232 return throwError(v8ReferenceError, "NPObject deleted", isolate); 233 234 NPVariant result; 235 VOID_TO_NPVARIANT(result); 236 if (!npObject->_class->getProperty(npObject, identifier, &result)) 237 return v8Undefined(); 238 239 v8::Handle<v8::Value> returnValue; 240 if (_NPN_IsAlive(npObject)) 241 returnValue = convertNPVariantToV8Object(&result, npObject, isolate); 242 _NPN_ReleaseVariantValue(&result); 243 return returnValue; 244 245 } 246 247 if (!_NPN_IsAlive(npObject)) 248 return throwError(v8ReferenceError, "NPObject deleted", isolate); 249 250 if (key->IsString() && npObject->_class->hasMethod && npObject->_class->hasMethod(npObject, identifier)) { 251 if (!_NPN_IsAlive(npObject)) 252 return throwError(v8ReferenceError, "NPObject deleted", isolate); 253 254 PrivateIdentifier* id = static_cast<PrivateIdentifier*>(identifier); 255 UnsafePersistent<v8::FunctionTemplate> functionTemplate = V8NPTemplateMap::sharedInstance(isolate).get(id); 256 // FunctionTemplate caches function for each context. 257 v8::Local<v8::Function> v8Function; 258 // Cache templates using identifier as the key. 259 if (functionTemplate.isEmpty()) { 260 // Create a new template. 261 v8::Local<v8::FunctionTemplate> temp = v8::FunctionTemplate::New(); 262 temp->SetCallHandler(npObjectMethodHandler, key); 263 V8NPTemplateMap::sharedInstance(isolate).set(id, temp); 264 v8Function = temp->GetFunction(); 265 } else { 266 v8Function = functionTemplate.newLocal(isolate)->GetFunction(); 267 } 268 v8Function->SetName(v8::Handle<v8::String>::Cast(key)); 269 return v8Function; 270 } 271 272 return v8Undefined(); 273 } 274 275 void npObjectNamedPropertyGetter(v8::Local<v8::String> name, const v8::PropertyCallbackInfo<v8::Value>& info) 276 { 277 NPIdentifier identifier = getStringIdentifier(name); 278 v8SetReturnValue(info, npObjectGetProperty(info.Holder(), identifier, name, info.GetIsolate())); 279 } 280 281 void npObjectIndexedPropertyGetter(uint32_t index, const v8::PropertyCallbackInfo<v8::Value>& info) 282 { 283 NPIdentifier identifier = _NPN_GetIntIdentifier(index); 284 v8SetReturnValue(info, npObjectGetProperty(info.Holder(), identifier, v8::Number::New(index), info.GetIsolate())); 285 } 286 287 void npObjectGetNamedProperty(v8::Local<v8::Object> self, v8::Local<v8::String> name, const v8::PropertyCallbackInfo<v8::Value>& info) 288 { 289 NPIdentifier identifier = getStringIdentifier(name); 290 v8SetReturnValue(info, npObjectGetProperty(self, identifier, name, info.GetIsolate())); 291 } 292 293 void npObjectGetIndexedProperty(v8::Local<v8::Object> self, uint32_t index, const v8::PropertyCallbackInfo<v8::Value>& info) 294 { 295 NPIdentifier identifier = _NPN_GetIntIdentifier(index); 296 v8SetReturnValue(info, npObjectGetProperty(self, identifier, v8::Number::New(index), info.GetIsolate())); 297 } 298 299 void npObjectQueryProperty(v8::Local<v8::String> name, const v8::PropertyCallbackInfo<v8::Integer>& info) 300 { 301 NPIdentifier identifier = getStringIdentifier(name); 302 if (npObjectGetProperty(info.Holder(), identifier, name, info.GetIsolate()).IsEmpty()) 303 return; 304 v8SetReturnValueInt(info, 0); 305 } 306 307 static v8::Handle<v8::Value> npObjectSetProperty(v8::Local<v8::Object> self, NPIdentifier identifier, v8::Local<v8::Value> value, v8::Isolate* isolate) 308 { 309 NPObject* npObject = v8ObjectToNPObject(self); 310 311 // Verify that our wrapper wasn't using a NPObject which has already been deleted. 312 if (!npObject || !_NPN_IsAlive(npObject)) { 313 throwError(v8ReferenceError, "NPObject deleted", isolate); 314 return value; // Intercepted, but an exception was thrown. 315 } 316 317 if (npObject->_class->hasProperty && npObject->_class->setProperty && npObject->_class->hasProperty(npObject, identifier)) { 318 if (!_NPN_IsAlive(npObject)) 319 return throwError(v8ReferenceError, "NPObject deleted", isolate); 320 321 NPVariant npValue; 322 VOID_TO_NPVARIANT(npValue); 323 convertV8ObjectToNPVariant(value, npObject, &npValue); 324 bool success = npObject->_class->setProperty(npObject, identifier, &npValue); 325 _NPN_ReleaseVariantValue(&npValue); 326 if (success) 327 return value; // Intercept the call. 328 } 329 return v8Undefined(); 330 } 331 332 333 void npObjectNamedPropertySetter(v8::Local<v8::String> name, v8::Local<v8::Value> value, const v8::PropertyCallbackInfo<v8::Value>& info) 334 { 335 NPIdentifier identifier = getStringIdentifier(name); 336 v8SetReturnValue(info, npObjectSetProperty(info.Holder(), identifier, value, info.GetIsolate())); 337 } 338 339 340 void npObjectIndexedPropertySetter(uint32_t index, v8::Local<v8::Value> value, const v8::PropertyCallbackInfo<v8::Value>& info) 341 { 342 NPIdentifier identifier = _NPN_GetIntIdentifier(index); 343 v8SetReturnValue(info, npObjectSetProperty(info.Holder(), identifier, value, info.GetIsolate())); 344 } 345 346 void npObjectSetNamedProperty(v8::Local<v8::Object> self, v8::Local<v8::String> name, v8::Local<v8::Value> value, const v8::PropertyCallbackInfo<v8::Value>& info) 347 { 348 NPIdentifier identifier = getStringIdentifier(name); 349 v8SetReturnValue(info, npObjectSetProperty(self, identifier, value, info.GetIsolate())); 350 } 351 352 void npObjectSetIndexedProperty(v8::Local<v8::Object> self, 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(self, identifier, value, info.GetIsolate())); 356 } 357 358 void npObjectPropertyEnumerator(const v8::PropertyCallbackInfo<v8::Array>& info, bool namedProperty) 359 { 360 NPObject* npObject = v8ObjectToNPObject(info.Holder()); 361 362 // Verify that our wrapper wasn't using a NPObject which 363 // has already been deleted. 364 if (!npObject || !_NPN_IsAlive(npObject)) 365 throwError(v8ReferenceError, "NPObject deleted", info.GetIsolate()); 366 367 if (NP_CLASS_STRUCT_VERSION_HAS_ENUM(npObject->_class) && npObject->_class->enumerate) { 368 uint32_t count; 369 NPIdentifier* identifiers; 370 if (npObject->_class->enumerate(npObject, &identifiers, &count)) { 371 v8::Handle<v8::Array> properties = v8::Array::New(count); 372 for (uint32_t i = 0; i < count; ++i) { 373 IdentifierRep* identifier = static_cast<IdentifierRep*>(identifiers[i]); 374 if (namedProperty) 375 properties->Set(v8::Integer::New(i, info.GetIsolate()), v8::String::NewSymbol(identifier->string())); 376 else 377 properties->Set(v8::Integer::New(i, info.GetIsolate()), v8::Integer::New(identifier->number(), info.GetIsolate())); 378 } 379 380 v8SetReturnValue(info, properties); 381 return; 382 } 383 } 384 } 385 386 void npObjectNamedPropertyEnumerator(const v8::PropertyCallbackInfo<v8::Array>& info) 387 { 388 npObjectPropertyEnumerator(info, true); 389 } 390 391 void npObjectIndexedPropertyEnumerator(const v8::PropertyCallbackInfo<v8::Array>& info) 392 { 393 npObjectPropertyEnumerator(info, false); 394 } 395 396 static DOMWrapperMap<NPObject>& staticNPObjectMap() 397 { 398 DEFINE_STATIC_LOCAL(DOMWrapperMap<NPObject>, npObjectMap, (v8::Isolate::GetCurrent())); 399 return npObjectMap; 400 } 401 402 template<> 403 inline void DOMWrapperMap<NPObject>::makeWeakCallback(v8::Isolate* isolate, v8::Persistent<v8::Object>* wrapper, DOMWrapperMap<NPObject>*) 404 { 405 NPObject* npObject = static_cast<NPObject*>(toNative(*wrapper)); 406 407 ASSERT(npObject); 408 ASSERT(staticNPObjectMap().get(npObject) == *wrapper); 409 410 // Must remove from our map before calling _NPN_ReleaseObject(). _NPN_ReleaseObject can 411 // call forgetV8ObjectForNPObject, which uses the table as well. 412 staticNPObjectMap().removeAndDispose(npObject); 413 414 if (_NPN_IsAlive(npObject)) 415 _NPN_ReleaseObject(npObject); 416 } 417 418 v8::Local<v8::Object> createV8ObjectForNPObject(NPObject* object, NPObject* root) 419 { 420 static v8::Persistent<v8::FunctionTemplate> npObjectDesc; 421 422 ASSERT(v8::Context::InContext()); 423 424 v8::Isolate* isolate = v8::Isolate::GetCurrent(); 425 426 // If this is a v8 object, just return it. 427 V8NPObject* v8NPObject = npObjectToV8NPObject(object); 428 if (v8NPObject) 429 return v8::Local<v8::Object>::New(isolate, v8NPObject->v8Object); 430 431 // If we've already wrapped this object, just return it. 432 v8::Handle<v8::Object> wrapper = staticNPObjectMap().get(object); 433 if (!wrapper.IsEmpty()) 434 return v8::Local<v8::Object>::New(isolate, wrapper); 435 436 // FIXME: we should create a Wrapper type as a subclass of JSObject. It has two internal fields, field 0 is the wrapped 437 // pointer, and field 1 is the type. There should be an api function that returns unused type id. The same Wrapper type 438 // can be used by DOM bindings. 439 if (npObjectDesc.IsEmpty()) { 440 v8::Handle<v8::FunctionTemplate> templ = v8::FunctionTemplate::New(); 441 templ->InstanceTemplate()->SetInternalFieldCount(npObjectInternalFieldCount); 442 templ->InstanceTemplate()->SetNamedPropertyHandler(npObjectNamedPropertyGetter, npObjectNamedPropertySetter, npObjectQueryProperty, 0, npObjectNamedPropertyEnumerator); 443 templ->InstanceTemplate()->SetIndexedPropertyHandler(npObjectIndexedPropertyGetter, npObjectIndexedPropertySetter, 0, 0, npObjectIndexedPropertyEnumerator); 444 templ->InstanceTemplate()->SetCallAsFunctionHandler(npObjectInvokeDefaultHandler); 445 npObjectDesc.Reset(isolate, templ); 446 } 447 448 // FIXME: Move staticNPObjectMap() to DOMDataStore. 449 // Use V8DOMWrapper::createWrapper() and 450 // V8DOMWrapper::associateObjectWithWrapper() 451 // to create a wrapper object. 452 v8::Handle<v8::Function> v8Function = v8::Local<v8::FunctionTemplate>::New(isolate, npObjectDesc)->GetFunction(); 453 v8::Local<v8::Object> value = V8ObjectConstructor::newInstance(v8Function); 454 if (value.IsEmpty()) 455 return value; 456 457 V8DOMWrapper::setNativeInfo(value, npObjectTypeInfo(), object); 458 459 // KJS retains the object as part of its wrapper (see Bindings::CInstance). 460 _NPN_RetainObject(object); 461 _NPN_RegisterObject(object, root); 462 463 WrapperConfiguration configuration = buildWrapperConfiguration(object, WrapperConfiguration::Dependent); 464 staticNPObjectMap().set(object, value, configuration); 465 ASSERT(V8DOMWrapper::maybeDOMWrapper(value)); 466 return value; 467 } 468 469 void forgetV8ObjectForNPObject(NPObject* object) 470 { 471 v8::Isolate* isolate = v8::Isolate::GetCurrent(); 472 v8::HandleScope scope(isolate); 473 v8::Handle<v8::Object> wrapper = staticNPObjectMap().getNewLocal(isolate, object); 474 if (!wrapper.IsEmpty()) { 475 V8DOMWrapper::clearNativeInfo(wrapper, npObjectTypeInfo()); 476 staticNPObjectMap().removeAndDispose(object); 477 _NPN_ReleaseObject(object); 478 } 479 } 480 481 } // namespace WebCore 482