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