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 "V8NPObject.h" 34 35 #include "HTMLPlugInElement.h" 36 #include "NPV8Object.h" 37 #include "V8DOMMap.h" 38 #include "V8HTMLAppletElement.h" 39 #include "V8HTMLEmbedElement.h" 40 #include "V8HTMLObjectElement.h" 41 #include "V8Helpers.h" 42 #include "V8NPUtils.h" 43 #include "V8Proxy.h" 44 #include "npruntime_impl.h" 45 #include "npruntime_priv.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 v8::Handle<v8::Value> npObjectInvokeImpl(const v8::Arguments& args, InvokeFunctionType functionId) 70 { 71 NPObject* npObject; 72 73 // These three types are subtypes of HTMLPlugInElement. 74 if (V8HTMLAppletElement::HasInstance(args.Holder()) || V8HTMLEmbedElement::HasInstance(args.Holder()) 75 || V8HTMLObjectElement::HasInstance(args.Holder())) { 76 // The holder object is a subtype of HTMLPlugInElement. 77 HTMLPlugInElement* element; 78 if (V8HTMLAppletElement::HasInstance(args.Holder())) 79 element = V8HTMLAppletElement::toNative(args.Holder()); 80 else if (V8HTMLEmbedElement::HasInstance(args.Holder())) 81 element = V8HTMLEmbedElement::toNative(args.Holder()); 82 else 83 element = V8HTMLObjectElement::toNative(args.Holder()); 84 ScriptInstance scriptInstance = element->getInstance(); 85 if (scriptInstance) 86 npObject = v8ObjectToNPObject(scriptInstance->instance()); 87 else 88 npObject = 0; 89 } else { 90 // The holder object is not a subtype of HTMLPlugInElement, it must be an NPObject which has three 91 // internal fields. 92 if (args.Holder()->InternalFieldCount() != npObjectInternalFieldCount) 93 return throwError("NPMethod called on non-NPObject", V8Proxy::ReferenceError); 94 95 npObject = v8ObjectToNPObject(args.Holder()); 96 } 97 98 // Verify that our wrapper wasn't using a NPObject which has already been deleted. 99 if (!npObject || !_NPN_IsAlive(npObject)) 100 return throwError("NPObject deleted", V8Proxy::ReferenceError); 101 102 // Wrap up parameters. 103 int numArgs = args.Length(); 104 OwnArrayPtr<NPVariant> npArgs = adoptArrayPtr(new NPVariant[numArgs]); 105 106 for (int i = 0; i < numArgs; i++) 107 convertV8ObjectToNPVariant(args[i], npObject, &npArgs[i]); 108 109 NPVariant result; 110 VOID_TO_NPVARIANT(result); 111 112 bool retval = true; 113 switch (functionId) { 114 case InvokeMethod: 115 if (npObject->_class->invoke) { 116 v8::Handle<v8::String> functionName(v8::String::Cast(*args.Data())); 117 NPIdentifier identifier = getStringIdentifier(functionName); 118 retval = npObject->_class->invoke(npObject, identifier, npArgs.get(), numArgs, &result); 119 } 120 break; 121 case InvokeConstruct: 122 if (npObject->_class->construct) 123 retval = npObject->_class->construct(npObject, npArgs.get(), numArgs, &result); 124 break; 125 case InvokeDefault: 126 if (npObject->_class->invokeDefault) 127 retval = npObject->_class->invokeDefault(npObject, npArgs.get(), numArgs, &result); 128 break; 129 default: 130 break; 131 } 132 133 if (!retval) 134 throwError("Error calling method on NPObject.", V8Proxy::GeneralError); 135 136 for (int i = 0; i < numArgs; i++) 137 _NPN_ReleaseVariantValue(&npArgs[i]); 138 139 // Unwrap return values. 140 v8::Handle<v8::Value> returnValue = convertNPVariantToV8Object(&result, npObject); 141 _NPN_ReleaseVariantValue(&result); 142 143 return returnValue; 144 } 145 146 147 v8::Handle<v8::Value> npObjectMethodHandler(const v8::Arguments& args) 148 { 149 return npObjectInvokeImpl(args, InvokeMethod); 150 } 151 152 153 v8::Handle<v8::Value> npObjectInvokeDefaultHandler(const v8::Arguments& args) 154 { 155 if (args.IsConstructCall()) 156 return npObjectInvokeImpl(args, InvokeConstruct); 157 158 return npObjectInvokeImpl(args, InvokeDefault); 159 } 160 161 162 static void weakTemplateCallback(v8::Persistent<v8::Value>, void* parameter); 163 164 // NPIdentifier is PrivateIdentifier*. 165 static WeakReferenceMap<PrivateIdentifier, v8::FunctionTemplate> staticTemplateMap(&weakTemplateCallback); 166 167 static void weakTemplateCallback(v8::Persistent<v8::Value> object, void* parameter) 168 { 169 PrivateIdentifier* identifier = static_cast<PrivateIdentifier*>(parameter); 170 ASSERT(identifier); 171 ASSERT(staticTemplateMap.contains(identifier)); 172 173 staticTemplateMap.forget(identifier); 174 } 175 176 177 static v8::Handle<v8::Value> npObjectGetProperty(v8::Local<v8::Object> self, NPIdentifier identifier, v8::Local<v8::Value> key) 178 { 179 NPObject* npObject = v8ObjectToNPObject(self); 180 181 // Verify that our wrapper wasn't using a NPObject which 182 // has already been deleted. 183 if (!npObject || !_NPN_IsAlive(npObject)) 184 return throwError("NPObject deleted", V8Proxy::ReferenceError); 185 186 187 if (npObject->_class->hasProperty && npObject->_class->hasProperty(npObject, identifier) 188 && npObject->_class->getProperty) { 189 190 NPVariant result; 191 VOID_TO_NPVARIANT(result); 192 if (!npObject->_class->getProperty(npObject, identifier, &result)) 193 return v8::Handle<v8::Value>(); 194 195 v8::Handle<v8::Value> returnValue = convertNPVariantToV8Object(&result, npObject); 196 _NPN_ReleaseVariantValue(&result); 197 return returnValue; 198 199 } 200 201 if (key->IsString() && npObject->_class->hasMethod && npObject->_class->hasMethod(npObject, identifier)) { 202 PrivateIdentifier* id = static_cast<PrivateIdentifier*>(identifier); 203 v8::Persistent<v8::FunctionTemplate> functionTemplate = staticTemplateMap.get(id); 204 // Cache templates using identifier as the key. 205 if (functionTemplate.IsEmpty()) { 206 // Create a new template. 207 v8::Local<v8::FunctionTemplate> temp = v8::FunctionTemplate::New(); 208 temp->SetCallHandler(npObjectMethodHandler, key); 209 functionTemplate = v8::Persistent<v8::FunctionTemplate>::New(temp); 210 staticTemplateMap.set(id, functionTemplate); 211 } 212 213 // FunctionTemplate caches function for each context. 214 v8::Local<v8::Function> v8Function = functionTemplate->GetFunction(); 215 v8Function->SetName(v8::Handle<v8::String>::Cast(key)); 216 return v8Function; 217 } 218 219 return v8::Handle<v8::Value>(); 220 } 221 222 v8::Handle<v8::Value> npObjectNamedPropertyGetter(v8::Local<v8::String> name, const v8::AccessorInfo& info) 223 { 224 NPIdentifier identifier = getStringIdentifier(name); 225 return npObjectGetProperty(info.Holder(), identifier, name); 226 } 227 228 v8::Handle<v8::Value> npObjectIndexedPropertyGetter(uint32_t index, const v8::AccessorInfo& info) 229 { 230 NPIdentifier identifier = _NPN_GetIntIdentifier(index); 231 return npObjectGetProperty(info.Holder(), identifier, v8::Number::New(index)); 232 } 233 234 v8::Handle<v8::Value> npObjectGetNamedProperty(v8::Local<v8::Object> self, v8::Local<v8::String> name) 235 { 236 NPIdentifier identifier = getStringIdentifier(name); 237 return npObjectGetProperty(self, identifier, name); 238 } 239 240 v8::Handle<v8::Value> npObjectGetIndexedProperty(v8::Local<v8::Object> self, uint32_t index) 241 { 242 NPIdentifier identifier = _NPN_GetIntIdentifier(index); 243 return npObjectGetProperty(self, identifier, v8::Number::New(index)); 244 } 245 246 v8::Handle<v8::Integer> npObjectQueryProperty(v8::Local<v8::String> name, const v8::AccessorInfo& info) 247 { 248 NPIdentifier identifier = getStringIdentifier(name); 249 return npObjectGetProperty(info.Holder(), identifier, name).IsEmpty() ? v8::Handle<v8::Integer>() : v8::Integer::New(v8::None); 250 } 251 252 static v8::Handle<v8::Value> npObjectSetProperty(v8::Local<v8::Object> self, NPIdentifier identifier, v8::Local<v8::Value> value) 253 { 254 NPObject* npObject = v8ObjectToNPObject(self); 255 256 // Verify that our wrapper wasn't using a NPObject which has already been deleted. 257 if (!npObject || !_NPN_IsAlive(npObject)) { 258 throwError("NPObject deleted", V8Proxy::ReferenceError); 259 return value; // Intercepted, but an exception was thrown. 260 } 261 262 if (npObject->_class->hasProperty && npObject->_class->hasProperty(npObject, identifier) 263 && npObject->_class->setProperty) { 264 265 NPVariant npValue; 266 VOID_TO_NPVARIANT(npValue); 267 convertV8ObjectToNPVariant(value, npObject, &npValue); 268 bool success = npObject->_class->setProperty(npObject, identifier, &npValue); 269 _NPN_ReleaseVariantValue(&npValue); 270 if (success) 271 return value; // Intercept the call. 272 } 273 return notHandledByInterceptor(); 274 } 275 276 277 v8::Handle<v8::Value> npObjectNamedPropertySetter(v8::Local<v8::String> name, v8::Local<v8::Value> value, const v8::AccessorInfo& info) 278 { 279 NPIdentifier identifier = getStringIdentifier(name); 280 return npObjectSetProperty(info.Holder(), identifier, value); 281 } 282 283 284 v8::Handle<v8::Value> npObjectIndexedPropertySetter(uint32_t index, v8::Local<v8::Value> value, const v8::AccessorInfo& info) 285 { 286 NPIdentifier identifier = _NPN_GetIntIdentifier(index); 287 return npObjectSetProperty(info.Holder(), identifier, value); 288 } 289 290 v8::Handle<v8::Value> npObjectSetNamedProperty(v8::Local<v8::Object> self, v8::Local<v8::String> name, v8::Local<v8::Value> value) 291 { 292 NPIdentifier identifier = getStringIdentifier(name); 293 return npObjectSetProperty(self, identifier, value); 294 } 295 296 v8::Handle<v8::Value> npObjectSetIndexedProperty(v8::Local<v8::Object> self, uint32_t index, v8::Local<v8::Value> value) 297 { 298 NPIdentifier identifier = _NPN_GetIntIdentifier(index); 299 return npObjectSetProperty(self, identifier, value); 300 } 301 302 v8::Handle<v8::Array> npObjectPropertyEnumerator(const v8::AccessorInfo& info, bool namedProperty) 303 { 304 NPObject* npObject = v8ObjectToNPObject(info.Holder()); 305 306 // Verify that our wrapper wasn't using a NPObject which 307 // has already been deleted. 308 if (!npObject || !_NPN_IsAlive(npObject)) 309 throwError("NPObject deleted", V8Proxy::ReferenceError); 310 311 if (NP_CLASS_STRUCT_VERSION_HAS_ENUM(npObject->_class) && npObject->_class->enumerate) { 312 uint32_t count; 313 NPIdentifier* identifiers; 314 if (npObject->_class->enumerate(npObject, &identifiers, &count)) { 315 v8::Handle<v8::Array> properties = v8::Array::New(count); 316 for (uint32_t i = 0; i < count; ++i) { 317 IdentifierRep* identifier = static_cast<IdentifierRep*>(identifiers[i]); 318 if (namedProperty) 319 properties->Set(v8::Integer::New(i), v8::String::New(identifier->string())); 320 else 321 properties->Set(v8::Integer::New(i), v8::Integer::New(identifier->number())); 322 } 323 324 return properties; 325 } 326 } 327 328 return v8::Handle<v8::Array>(); 329 } 330 331 v8::Handle<v8::Array> npObjectNamedPropertyEnumerator(const v8::AccessorInfo& info) 332 { 333 return npObjectPropertyEnumerator(info, true); 334 } 335 336 v8::Handle<v8::Array> npObjectIndexedPropertyEnumerator(const v8::AccessorInfo& info) 337 { 338 return npObjectPropertyEnumerator(info, false); 339 } 340 341 static void weakNPObjectCallback(v8::Persistent<v8::Value>, void* parameter); 342 343 static DOMWrapperMap<NPObject> staticNPObjectMap(&weakNPObjectCallback); 344 345 static void weakNPObjectCallback(v8::Persistent<v8::Value> object, void* parameter) 346 { 347 NPObject* npObject = static_cast<NPObject*>(parameter); 348 ASSERT(staticNPObjectMap.contains(npObject)); 349 ASSERT(npObject); 350 351 // Must remove from our map before calling _NPN_ReleaseObject(). _NPN_ReleaseObject can call ForgetV8ObjectForNPObject, which 352 // uses the table as well. 353 staticNPObjectMap.forget(npObject); 354 355 if (_NPN_IsAlive(npObject)) 356 _NPN_ReleaseObject(npObject); 357 } 358 359 360 v8::Local<v8::Object> createV8ObjectForNPObject(NPObject* object, NPObject* root) 361 { 362 static v8::Persistent<v8::FunctionTemplate> npObjectDesc; 363 364 ASSERT(v8::Context::InContext()); 365 366 // If this is a v8 object, just return it. 367 if (object->_class == npScriptObjectClass) { 368 V8NPObject* v8NPObject = reinterpret_cast<V8NPObject*>(object); 369 return v8::Local<v8::Object>::New(v8NPObject->v8Object); 370 } 371 372 // If we've already wrapped this object, just return it. 373 if (staticNPObjectMap.contains(object)) 374 return v8::Local<v8::Object>::New(staticNPObjectMap.get(object)); 375 376 // FIXME: we should create a Wrapper type as a subclass of JSObject. It has two internal fields, field 0 is the wrapped 377 // pointer, and field 1 is the type. There should be an api function that returns unused type id. The same Wrapper type 378 // can be used by DOM bindings. 379 if (npObjectDesc.IsEmpty()) { 380 npObjectDesc = v8::Persistent<v8::FunctionTemplate>::New(v8::FunctionTemplate::New()); 381 npObjectDesc->InstanceTemplate()->SetInternalFieldCount(npObjectInternalFieldCount); 382 npObjectDesc->InstanceTemplate()->SetNamedPropertyHandler(npObjectNamedPropertyGetter, npObjectNamedPropertySetter, npObjectQueryProperty, 0, npObjectNamedPropertyEnumerator); 383 npObjectDesc->InstanceTemplate()->SetIndexedPropertyHandler(npObjectIndexedPropertyGetter, npObjectIndexedPropertySetter, 0, 0, npObjectIndexedPropertyEnumerator); 384 npObjectDesc->InstanceTemplate()->SetCallAsFunctionHandler(npObjectInvokeDefaultHandler); 385 } 386 387 v8::Handle<v8::Function> v8Function = npObjectDesc->GetFunction(); 388 v8::Local<v8::Object> value = SafeAllocation::newInstance(v8Function); 389 390 // If we were unable to allocate the instance, we avoid wrapping and registering the NP object. 391 if (value.IsEmpty()) 392 return value; 393 394 V8DOMWrapper::setDOMWrapper(value, npObjectTypeInfo(), object); 395 396 // KJS retains the object as part of its wrapper (see Bindings::CInstance). 397 _NPN_RetainObject(object); 398 399 _NPN_RegisterObject(object, root); 400 401 // Maintain a weak pointer for v8 so we can cleanup the object. 402 v8::Persistent<v8::Object> weakRef = v8::Persistent<v8::Object>::New(value); 403 staticNPObjectMap.set(object, weakRef); 404 405 return value; 406 } 407 408 void forgetV8ObjectForNPObject(NPObject* object) 409 { 410 if (staticNPObjectMap.contains(object)) { 411 v8::HandleScope scope; 412 v8::Persistent<v8::Object> handle(staticNPObjectMap.get(object)); 413 V8DOMWrapper::setDOMWrapper(handle, npObjectTypeInfo(), 0); 414 staticNPObjectMap.forget(object); 415 _NPN_ReleaseObject(object); 416 } 417 } 418 419 } // namespace WebCore 420