Chromium Code Reviews| OLD | NEW |
|---|---|
| (Empty) | |
| 1 // Copyright (c) 2011 The Chromium Authors. All rights reserved. | |
| 2 // Use of this source code is governed by a BSD-style license that can be | |
| 3 // found in the LICENSE file. | |
| 4 | |
| 5 #include "content/browser/renderer_host/java/java_bound_object.h" | |
| 6 | |
| 7 #include "base/android/jni_android.h" | |
| 8 #include "base/android/jni_string.h" | |
| 9 #include "base/string_number_conversions.h" | |
| 10 #include "base/stringprintf.h" | |
| 11 #include "third_party/WebKit/Source/WebKit/chromium/public/WebBindings.h" | |
| 12 | |
| 13 using base::StringPrintf; | |
| 14 using base::android::AttachCurrentThread; | |
| 15 using base::android::ConvertUTF8ToJavaString; | |
| 16 using base::android::GetMethodIDFromClassName; | |
| 17 using base::android::JavaRef; | |
| 18 using base::android::ScopedJavaGlobalRef; | |
| 19 using base::android::ScopedJavaLocalRef; | |
| 20 using WebKit::WebBindings; | |
| 21 | |
| 22 // The conversion between JavaScript and Java types is based on the Live | |
| 23 // Connect 2 spec. See | |
| 24 // http://jdk6.java.net/plugin2/liveconnect/#JS_JAVA_CONVERSIONS. | |
| 25 | |
| 26 // Note that in some cases, we differ from from the spec in order to maintain | |
| 27 // existing behavior. These areas are marked LIVECONNECT_COMPLIANCE. We may | |
| 28 // revisit this decision in the future. | |
| 29 | |
| 30 namespace { | |
| 31 | |
| 32 std::string NPIdentifierToString(NPIdentifier identifier) { | |
| 33 const NPUTF8* string; | |
| 34 int32_t number; | |
| 35 bool is_string; | |
| 36 WebBindings::extractIdentifierData(identifier, string, number, is_string); | |
| 37 DCHECK(is_string); | |
| 38 return string; | |
| 39 } | |
| 40 | |
| 41 // Our special NPObject type. We extend an NPObject with a pointer to a | |
| 42 // JavaBoundObject. We also add static methods for each of the NPObject | |
| 43 // callbacks, which are registered by our NPClass. These methods simply | |
| 44 // delegate to the private implementation methods of JavaBoundObject. | |
| 45 struct JavaNPObject : public NPObject { | |
| 46 JavaBoundObject* bound_object; | |
| 47 | |
| 48 static const NPClass kNPClass; | |
| 49 | |
| 50 static NPObject* Allocate(NPP npp, NPClass* np_class); | |
| 51 static void Deallocate(NPObject* np_object); | |
| 52 static bool HasMethod(NPObject* np_object, NPIdentifier np_identifier); | |
| 53 static bool Invoke(NPObject* np_object, NPIdentifier np_identifier, | |
| 54 const NPVariant *args, uint32_t arg_count, | |
| 55 NPVariant *result); | |
| 56 static bool HasProperty(NPObject* np_object, NPIdentifier np_identifier); | |
| 57 static bool GetProperty(NPObject* np_object, NPIdentifier np_identifier, | |
| 58 NPVariant *result); | |
| 59 }; | |
| 60 | |
| 61 const NPClass JavaNPObject::kNPClass = { | |
| 62 NP_CLASS_STRUCT_VERSION, | |
| 63 JavaNPObject::Allocate, | |
| 64 JavaNPObject::Deallocate, | |
| 65 NULL, // NPInvalidate | |
| 66 JavaNPObject::HasMethod, | |
| 67 JavaNPObject::Invoke, | |
| 68 NULL, // NPInvokeDefault | |
| 69 JavaNPObject::HasProperty, | |
| 70 JavaNPObject::GetProperty, | |
| 71 NULL, // NPSetProperty, | |
| 72 NULL, // NPRemoveProperty | |
| 73 }; | |
| 74 | |
| 75 NPObject* JavaNPObject::Allocate(NPP npp, NPClass* np_class) { | |
| 76 JavaNPObject* obj = new JavaNPObject; | |
| 77 return obj; | |
| 78 } | |
| 79 | |
| 80 void JavaNPObject::Deallocate(NPObject* np_object) { | |
| 81 JavaNPObject* obj = reinterpret_cast<JavaNPObject*>(np_object); | |
| 82 delete obj->bound_object; | |
| 83 delete obj; | |
| 84 } | |
| 85 | |
| 86 bool JavaNPObject::HasMethod(NPObject* np_object, NPIdentifier np_identifier) { | |
| 87 std::string name = NPIdentifierToString(np_identifier); | |
| 88 JavaNPObject* obj = reinterpret_cast<JavaNPObject*>(np_object); | |
| 89 return obj->bound_object->HasMethod(name); | |
| 90 } | |
| 91 | |
| 92 bool JavaNPObject::Invoke(NPObject* np_object, NPIdentifier np_identifier, | |
| 93 const NPVariant* args, uint32_t arg_count, | |
| 94 NPVariant* result) { | |
| 95 std::string name = NPIdentifierToString(np_identifier); | |
| 96 JavaNPObject* obj = reinterpret_cast<JavaNPObject*>(np_object); | |
| 97 return obj->bound_object->Invoke(name, args, arg_count, result); | |
| 98 } | |
| 99 | |
| 100 bool JavaNPObject::HasProperty(NPObject* np_object, | |
| 101 NPIdentifier np_identifier) { | |
| 102 // LIVECONNECT_COMPLIANCE: Return false to indicate that the property is not | |
| 103 // present. We should support this correctly. | |
| 104 return false; | |
| 105 } | |
| 106 | |
| 107 bool JavaNPObject::GetProperty(NPObject* np_object, | |
| 108 NPIdentifier np_identifier, | |
| 109 NPVariant* result) { | |
| 110 // LIVECONNECT_COMPLIANCE: Return false to indicate that the property is | |
| 111 // undefined. We should support this correctly. | |
| 112 return false; | |
| 113 } | |
| 114 | |
| 115 // Calls a Java method through JNI and returns the result as an NPVariant. Note | |
| 116 // that this method does not do any type coercion. The Java return value is | |
| 117 // simply converted to the corresponding NPAPI type. | |
| 118 NPVariant CallJNIMethod(jobject object, JavaType::Type return_type, | |
| 119 jmethodID id, jvalue* parameters) { | |
| 120 JNIEnv* env = AttachCurrentThread(); | |
| 121 NPVariant result; | |
| 122 switch (return_type) { | |
| 123 case JavaType::TypeBoolean: | |
| 124 BOOLEAN_TO_NPVARIANT(env->CallBooleanMethodA(object, id, parameters), | |
| 125 result); | |
| 126 break; | |
| 127 case JavaType::TypeByte: | |
| 128 INT32_TO_NPVARIANT(env->CallByteMethodA(object, id, parameters), result); | |
| 129 break; | |
| 130 case JavaType::TypeChar: | |
| 131 INT32_TO_NPVARIANT(env->CallCharMethodA(object, id, parameters), result); | |
| 132 break; | |
| 133 case JavaType::TypeShort: | |
| 134 INT32_TO_NPVARIANT(env->CallShortMethodA(object, id, parameters), result); | |
| 135 break; | |
| 136 case JavaType::TypeInt: | |
| 137 INT32_TO_NPVARIANT(env->CallIntMethodA(object, id, parameters), result); | |
| 138 break; | |
| 139 case JavaType::TypeLong: | |
| 140 DOUBLE_TO_NPVARIANT(env->CallLongMethodA(object, id, parameters), result); | |
| 141 break; | |
| 142 case JavaType::TypeFloat: | |
| 143 DOUBLE_TO_NPVARIANT(env->CallFloatMethodA(object, id, parameters), | |
| 144 result); | |
| 145 break; | |
| 146 case JavaType::TypeDouble: | |
| 147 DOUBLE_TO_NPVARIANT(env->CallDoubleMethodA(object, id, parameters), | |
| 148 result); | |
| 149 break; | |
| 150 case JavaType::TypeVoid: | |
| 151 env->CallVoidMethodA(object, id, parameters); | |
| 152 VOID_TO_NPVARIANT(result); | |
| 153 break; | |
| 154 case JavaType::TypeArray: | |
| 155 // TODO(steveblock): Handle arrays | |
| 156 VOID_TO_NPVARIANT(result); | |
| 157 break; | |
| 158 case JavaType::TypeString: { | |
| 159 ScopedJavaLocalRef<jstring> java_string(env, static_cast<jstring>( | |
| 160 env->CallObjectMethodA(object, id, parameters))); | |
| 161 if (!java_string.obj()) { | |
| 162 // LIVECONNECT_COMPLIANCE: Return undefined to maintain existing | |
| 163 // behavior. We should return a null string. | |
| 164 VOID_TO_NPVARIANT(result); | |
| 165 break; | |
| 166 } | |
| 167 std::string str = | |
| 168 base::android::ConvertJavaStringToUTF8(env, java_string.obj()); | |
| 169 // Take a copy and pass ownership to the variant. We must allocate using | |
| 170 // NPN_MemAlloc, to match NPN_ReleaseVariant, which uses NPN_MemFree. | |
| 171 size_t length = str.length(); | |
| 172 char* buffer = static_cast<char*>(NPN_MemAlloc(length)); | |
| 173 str.copy(buffer, length, 0); | |
| 174 STRINGN_TO_NPVARIANT(buffer, length, result); | |
| 175 break; | |
| 176 } | |
| 177 case JavaType::TypeObject: { | |
| 178 ScopedJavaLocalRef<jobject> java_object( | |
| 179 env, | |
| 180 env->CallObjectMethodA(object, id, parameters)); | |
| 181 if (!java_object.obj()) { | |
| 182 NULL_TO_NPVARIANT(result); | |
| 183 break; | |
| 184 } | |
| 185 OBJECT_TO_NPVARIANT(JavaBoundObject::Create(java_object), result); | |
| 186 break; | |
| 187 } | |
| 188 } | |
| 189 return result; | |
| 190 } | |
| 191 | |
| 192 jvalue CoerceJavaScriptNumberToJavaValue(const NPVariant& variant, | |
| 193 JavaType::Type target_type) { | |
| 194 // See http://jdk6.java.net/plugin2/liveconnect/#JS_NUMBER_VALUES. | |
| 195 jvalue result; | |
| 196 DCHECK(variant.type == NPVariantType_Int32 || | |
| 197 variant.type == NPVariantType_Double); | |
| 198 bool is_double = variant.type == NPVariantType_Double; | |
| 199 switch (target_type) { | |
| 200 case JavaType::TypeByte: | |
| 201 result.b = is_double ? static_cast<jbyte>(NPVARIANT_TO_DOUBLE(variant)) : | |
| 202 static_cast<jbyte>(NPVARIANT_TO_INT32(variant)); | |
| 203 break; | |
| 204 case JavaType::TypeChar: | |
| 205 // LIVECONNECT_COMPLIANCE: Convert double to 0 to maintain existing | |
| 206 // behavior. | |
| 207 result.c = is_double ? 0 : | |
| 208 static_cast<jchar>(NPVARIANT_TO_INT32(variant)); | |
| 209 break; | |
| 210 case JavaType::TypeShort: | |
| 211 result.s = is_double ? static_cast<jshort>(NPVARIANT_TO_DOUBLE(variant)) : | |
| 212 static_cast<jshort>(NPVARIANT_TO_INT32(variant)); | |
| 213 break; | |
| 214 case JavaType::TypeInt: | |
| 215 result.i = is_double ? static_cast<jint>(NPVARIANT_TO_DOUBLE(variant)) : | |
| 216 NPVARIANT_TO_INT32(variant); | |
| 217 break; | |
| 218 case JavaType::TypeLong: | |
| 219 result.j = is_double ? static_cast<jlong>(NPVARIANT_TO_DOUBLE(variant)) : | |
| 220 NPVARIANT_TO_INT32(variant); | |
| 221 break; | |
| 222 case JavaType::TypeFloat: | |
| 223 result.f = is_double ? static_cast<jfloat>(NPVARIANT_TO_DOUBLE(variant)) : | |
| 224 NPVARIANT_TO_INT32(variant); | |
| 225 break; | |
| 226 case JavaType::TypeDouble: | |
| 227 result.d = is_double ? NPVARIANT_TO_DOUBLE(variant) : | |
| 228 NPVARIANT_TO_INT32(variant); | |
| 229 break; | |
| 230 case JavaType::TypeObject: | |
| 231 // LIVECONNECT_COMPLIANCE: Convert to null to maintain existing behavior. | |
| 232 // We should handle object equivalents of primitive types. | |
| 233 result.l = NULL; | |
| 234 break; | |
| 235 case JavaType::TypeString: | |
| 236 result.l = ConvertUTF8ToJavaString( | |
| 237 AttachCurrentThread(), | |
| 238 is_double ? StringPrintf("%.6lg", NPVARIANT_TO_DOUBLE(variant)) : | |
| 239 base::IntToString(NPVARIANT_TO_INT32(variant))); | |
| 240 break; | |
| 241 case JavaType::TypeBoolean: | |
| 242 // LIVECONNECT_COMPLIANCE: Convert to false to maintain existing behavior. | |
| 243 // We should convert to false for o or NaN, true otherwise. | |
| 244 result.z = JNI_FALSE; | |
| 245 break; | |
| 246 case JavaType::TypeArray: | |
| 247 // LIVECONNECT_COMPLIANCE: Convert to null to maintain existing behavior. | |
| 248 // We should raise a JavaScript exception. | |
| 249 result.l = NULL; | |
| 250 break; | |
| 251 case JavaType::TypeVoid: | |
| 252 // Conversion to void must never happen. | |
| 253 NOTREACHED(); | |
| 254 break; | |
| 255 } | |
| 256 return result; | |
| 257 } | |
| 258 | |
| 259 jvalue CoerceJavaScriptBooleanToJavaValue(const NPVariant& variant, | |
| 260 JavaType::Type target_type) { | |
| 261 // See http://jdk6.java.net/plugin2/liveconnect/#JS_BOOLEAN_VALUES. | |
| 262 DCHECK_EQ(NPVariantType_Bool, variant.type); | |
| 263 bool boolean_value = NPVARIANT_TO_BOOLEAN(variant); | |
| 264 jvalue result; | |
| 265 switch (target_type) { | |
| 266 case JavaType::TypeBoolean: | |
| 267 result.z = boolean_value ? JNI_TRUE : JNI_FALSE; | |
| 268 break; | |
| 269 case JavaType::TypeObject: | |
| 270 // LIVECONNECT_COMPLIANCE: Convert to NULL to maintain existing behavior. | |
| 271 // We should handle java.lang.Boolean and java.lang.Object. | |
| 272 result.l = NULL; | |
| 273 break; | |
| 274 case JavaType::TypeString: | |
| 275 result.l = ConvertUTF8ToJavaString(AttachCurrentThread(), | |
| 276 boolean_value ? "true" : "false"); | |
| 277 break; | |
| 278 case JavaType::TypeByte: | |
| 279 case JavaType::TypeChar: | |
| 280 case JavaType::TypeShort: | |
| 281 case JavaType::TypeInt: | |
| 282 case JavaType::TypeLong: | |
| 283 case JavaType::TypeFloat: | |
| 284 case JavaType::TypeDouble: { | |
| 285 // LIVECONNECT_COMPLIANCE: Convert to 0 to maintain existing behavior. We | |
| 286 // should convert to 0 or 1. | |
| 287 jvalue null_value = {0}; | |
| 288 result = null_value; | |
| 289 break; | |
| 290 } | |
| 291 case JavaType::TypeArray: | |
| 292 // LIVECONNECT_COMPLIANCE: Convert to NULL to maintain existing behavior. | |
| 293 // We should raise a JavaScript exception. | |
| 294 result.l = NULL; | |
| 295 break; | |
| 296 case JavaType::TypeVoid: | |
| 297 // Conversion to void must never happen. | |
| 298 NOTREACHED(); | |
| 299 break; | |
| 300 } | |
| 301 return result; | |
| 302 } | |
| 303 | |
| 304 jvalue CoerceJavaScriptStringToJavaValue(const NPVariant& variant, | |
| 305 JavaType::Type target_type) { | |
| 306 // See http://jdk6.java.net/plugin2/liveconnect/#JS_STRING_VALUES. | |
| 307 DCHECK_EQ(NPVariantType_String, variant.type); | |
| 308 jvalue result; | |
| 309 switch (target_type) { | |
| 310 case JavaType::TypeString: | |
| 311 result.l = ConvertUTF8ToJavaString( | |
| 312 AttachCurrentThread(), | |
| 313 base::StringPiece(NPVARIANT_TO_STRING(variant).UTF8Characters, | |
| 314 NPVARIANT_TO_STRING(variant).UTF8Length)); | |
| 315 break; | |
| 316 case JavaType::TypeObject: | |
| 317 // LIVECONNECT_COMPLIANCE: Convert to NULL to maintain existing behavior. | |
| 318 // We should handle java.lang.Object. | |
| 319 result.l = NULL; | |
| 320 break; | |
| 321 case JavaType::TypeByte: | |
| 322 case JavaType::TypeShort: | |
| 323 case JavaType::TypeInt: | |
| 324 case JavaType::TypeLong: | |
| 325 case JavaType::TypeFloat: | |
| 326 case JavaType::TypeDouble: { | |
| 327 // LIVECONNECT_COMPLIANCE: Convert to 0 to maintain existing behavior. we | |
| 328 // should use valueOf() method of corresponding object type. | |
| 329 jvalue null_value = {0}; | |
| 330 result = null_value; | |
| 331 break; | |
| 332 } | |
| 333 case JavaType::TypeChar: | |
| 334 // LIVECONNECT_COMPLIANCE: Convert to 0 to maintain existing behavior. we | |
| 335 // should use java.lang.Short.decode(). | |
| 336 result.c = 0; | |
| 337 break; | |
| 338 case JavaType::TypeBoolean: | |
| 339 // LIVECONNECT_COMPLIANCE: Convert to false to maintain existing behavior. | |
| 340 // We should convert the empty string to false, otherwise true. | |
| 341 result.z = JNI_FALSE; | |
| 342 break; | |
| 343 case JavaType::TypeArray: | |
| 344 // LIVECONNECT_COMPLIANCE: Convert to NULL to maintain existing behavior. | |
| 345 // We should raise a JavaScript exception. | |
| 346 result.l = NULL; | |
| 347 break; | |
| 348 case JavaType::TypeVoid: | |
| 349 // Conversion to void must never happen. | |
| 350 NOTREACHED(); | |
| 351 break; | |
| 352 } | |
| 353 return result; | |
| 354 } | |
| 355 | |
| 356 jvalue CoerceJavaScriptObjectToJavaValue(const NPVariant& variant, | |
| 357 JavaType::Type target_type) { | |
| 358 // We only handle Java objects. See | |
| 359 // http://jdk6.java.net/plugin2/liveconnect/#JS_JAVA_OBJECTS. | |
| 360 // TODO(steveblock): Handle arrays. | |
| 361 // TODO(steveblock): Handle JavaScript objects. | |
| 362 DCHECK_EQ(NPVariantType_Object, variant.type); | |
| 363 | |
| 364 // The only type of object we should encounter is a Java object, as | |
| 365 // other objects should have been converted to NULL in the renderer. | |
| 366 // See CreateNPVariantParam(). | |
| 367 // TODO(steveblock): This will have to change once we support arrays and | |
| 368 // JavaScript objects. | |
| 369 NPObject* object = NPVARIANT_TO_OBJECT(variant); | |
| 370 DCHECK_EQ(&JavaNPObject::kNPClass, object->_class); | |
| 371 | |
| 372 jvalue result; | |
| 373 switch (target_type) { | |
| 374 case JavaType::TypeObject: | |
| 375 // LIVECONNECT_COMPLIANCE: Pass all Java objects to maintain existing | |
| 376 // behavior. We should pass only Java objects which are | |
| 377 // assignment-compatibile. | |
| 378 result.l = AttachCurrentThread()->NewLocalRef( | |
| 379 JavaBoundObject::GetJavaObject(object)); | |
| 380 break; | |
| 381 case JavaType::TypeString: | |
| 382 // LIVECONNECT_COMPLIANCE: Convert to "undefined" to maintain existing | |
| 383 // behavior. We should call toString() on the Java object. | |
| 384 result.l = ConvertUTF8ToJavaString(AttachCurrentThread(), "undefined"); | |
| 385 break; | |
| 386 case JavaType::TypeByte: | |
| 387 case JavaType::TypeShort: | |
| 388 case JavaType::TypeInt: | |
| 389 case JavaType::TypeLong: | |
| 390 case JavaType::TypeFloat: | |
| 391 case JavaType::TypeDouble: | |
| 392 case JavaType::TypeChar: { | |
| 393 // LIVECONNECT_COMPLIANCE: Convert to 0 to maintain existing behavior. We | |
| 394 // should raise a JavaScript exception. | |
| 395 jvalue null_value = {0}; | |
| 396 result = null_value; | |
| 397 break; | |
| 398 } | |
| 399 case JavaType::TypeBoolean: | |
| 400 // LIVECONNECT_COMPLIANCE: Convert to false to maintain existing behavior. | |
| 401 // We should raise a JavaScript exception. | |
| 402 result.z = JNI_FALSE; | |
| 403 break; | |
| 404 case JavaType::TypeArray: | |
| 405 // LIVECONNECT_COMPLIANCE: Convert to NULL to maintain existing behavior. | |
| 406 // We should raise a JavaScript exception. | |
| 407 result.l = NULL; | |
| 408 break; | |
| 409 case JavaType::TypeVoid: | |
| 410 // Conversion to void must never happen. | |
| 411 NOTREACHED(); | |
| 412 break; | |
| 413 } | |
| 414 return result; | |
| 415 } | |
| 416 | |
| 417 jvalue CoerceJavaScriptNullOrUndefinedToJavaValue(const NPVariant& variant, | |
| 418 JavaType::Type target_type) { | |
| 419 // See http://jdk6.java.net/plugin2/liveconnect/#JS_NULL. | |
| 420 DCHECK(variant.type == NPVariantType_Null || | |
| 421 variant.type == NPVariantType_Void); | |
| 422 jvalue result; | |
| 423 switch (target_type) { | |
| 424 case JavaType::TypeObject: | |
| 425 result.l = NULL; | |
| 426 break; | |
| 427 case JavaType::TypeString: | |
| 428 if (variant.type == NPVariantType_Void) { | |
| 429 // LIVECONNECT_COMPLIANCE: Convert undefined to "undefined" to maintain | |
| 430 // existing behavior. We should convert undefined to NULL. | |
| 431 result.l = ConvertUTF8ToJavaString(AttachCurrentThread(), "undefined"); | |
| 432 } else { | |
| 433 result.l = NULL; | |
| 434 } | |
| 435 break; | |
| 436 case JavaType::TypeByte: | |
| 437 case JavaType::TypeChar: | |
| 438 case JavaType::TypeShort: | |
| 439 case JavaType::TypeInt: | |
| 440 case JavaType::TypeLong: | |
| 441 case JavaType::TypeFloat: | |
| 442 case JavaType::TypeDouble: { | |
| 443 jvalue null_value = {0}; | |
| 444 result = null_value; | |
| 445 break; | |
| 446 } | |
| 447 case JavaType::TypeBoolean: | |
| 448 result.z = JNI_FALSE; | |
| 449 break; | |
| 450 case JavaType::TypeArray: | |
| 451 // LIVECONNECT_COMPLIANCE: Convert to NULL to maintain existing behavior. | |
| 452 // We should raise a JavaScript exception. | |
| 453 result.l = NULL; | |
| 454 break; | |
| 455 case JavaType::TypeVoid: | |
| 456 // Conversion to void must never happen. | |
| 457 NOTREACHED(); | |
| 458 break; | |
| 459 } | |
| 460 return result; | |
| 461 } | |
| 462 | |
| 463 jvalue CoerceJavaScriptValueToJavaValue(const NPVariant& variant, | |
| 464 JavaType::Type target_type) { | |
| 465 // Note that in all these conversions, the relevant field of the jvalue must | |
| 466 // always be explicitly set, as jvalue does not initialize its fields. | |
| 467 | |
| 468 // Some of these methods create new Java Strings. Note that we don't | |
| 469 // explicitly release the local ref to these new objects, as there's no simple | |
| 470 // way to do so. | |
| 471 switch (variant.type) { | |
| 472 case NPVariantType_Int32: | |
| 473 case NPVariantType_Double: | |
| 474 return CoerceJavaScriptNumberToJavaValue(variant, target_type); | |
| 475 case NPVariantType_Bool: | |
| 476 return CoerceJavaScriptBooleanToJavaValue(variant, target_type); | |
| 477 case NPVariantType_String: | |
| 478 return CoerceJavaScriptStringToJavaValue(variant, target_type); | |
| 479 case NPVariantType_Object: | |
| 480 return CoerceJavaScriptObjectToJavaValue(variant, target_type); | |
| 481 case NPVariantType_Null: | |
| 482 case NPVariantType_Void: | |
| 483 return CoerceJavaScriptNullOrUndefinedToJavaValue(variant, target_type); | |
| 484 } | |
| 485 NOTREACHED(); | |
| 486 return jvalue(); | |
| 487 } | |
| 488 | |
| 489 static jmethodID g_object_get_class_id = NULL; | |
| 490 jmethodID GetObjectGetClassID() { | |
| 491 if (!g_object_get_class_id) { | |
| 492 g_object_get_class_id = GetMethodIDFromClassName(AttachCurrentThread(), | |
| 493 "java/lang/Object", | |
| 494 "getClass", | |
| 495 "()Ljava/lang/Class;"); | |
| 496 } | |
| 497 return g_object_get_class_id; | |
| 498 } | |
| 499 | |
| 500 static jmethodID g_class_get_methods_id = NULL; | |
| 501 jmethodID ClassGetMethodsID() { | |
| 502 if (!g_class_get_methods_id) { | |
| 503 g_class_get_methods_id = GetMethodIDFromClassName( | |
| 504 AttachCurrentThread(), | |
| 505 "java/lang/Class", | |
| 506 "getMethods", | |
| 507 "()[Ljava/lang/reflect/Method;"); | |
| 508 } | |
| 509 return g_class_get_methods_id; | |
| 510 } | |
| 511 | |
| 512 } // namespace | |
| 513 | |
| 514 | |
| 515 NPObject* JavaBoundObject::Create(const JavaRef<jobject>& object) { | |
| 516 // The first argument (a plugin's instance handle) is passed through to the | |
| 517 // allocate function directly, and we don't use it, so it's ok to be 0. | |
| 518 // The object is created with a ref count of one. | |
| 519 NPObject* np_object = WebBindings::createObject(0, const_cast<NPClass*>( | |
| 520 &JavaNPObject::kNPClass)); | |
| 521 // The NPObject takes ownership of the JavaBoundObject. | |
| 522 reinterpret_cast<JavaNPObject*>(np_object)->bound_object = | |
| 523 new JavaBoundObject(object); | |
| 524 return np_object; | |
| 525 } | |
| 526 | |
| 527 JavaBoundObject::JavaBoundObject(const JavaRef<jobject>& object) | |
| 528 : java_object_(object), | |
| 529 are_methods_set_up_(false) { | |
| 530 // We don't do anything with our Java object when first created. We do it all | |
| 531 // lazily when a method is first invoked. | |
| 532 } | |
| 533 | |
| 534 JavaBoundObject::~JavaBoundObject() { | |
| 535 // Use the current thread's JNI env to release our global ref to the Java | |
| 536 // object. | |
|
joth
2011/11/11 11:40:38
is this comment suggesting that this class is crea
Steve Block
2011/11/11 12:22:53
Yes, currently objects of this class are created o
| |
| 537 AttachCurrentThread()->DeleteGlobalRef(java_object_.Release()); | |
| 538 } | |
| 539 | |
| 540 jobject JavaBoundObject::GetJavaObject(NPObject* object) { | |
| 541 DCHECK_EQ(&JavaNPObject::kNPClass, object->_class); | |
| 542 JavaBoundObject* jbo = reinterpret_cast<JavaNPObject*>(object)->bound_object; | |
| 543 return jbo->java_object_.obj(); | |
| 544 } | |
| 545 | |
| 546 bool JavaBoundObject::HasMethod(const std::string& name) const { | |
| 547 const_cast<JavaBoundObject*>(this)->EnsureMethodsAreSetUp(); | |
| 548 return methods_.find(name) != methods_.end(); | |
| 549 } | |
| 550 | |
| 551 bool JavaBoundObject::Invoke(const std::string& name, const NPVariant* args, | |
| 552 size_t arg_count, NPVariant* result) { | |
| 553 EnsureMethodsAreSetUp(); | |
| 554 | |
| 555 // Get all methods with the correct name. | |
| 556 std::pair<JavaMethodMap::const_iterator, JavaMethodMap::const_iterator> | |
| 557 iters = methods_.equal_range(name); | |
| 558 if (iters.first == iters.second) { | |
| 559 return false; | |
| 560 } | |
| 561 | |
| 562 // Take the first method with the correct number of arguments. | |
| 563 JavaMethod* method = NULL; | |
| 564 for (JavaMethodMap::const_iterator iter = iters.first; iter != iters.second; | |
| 565 ++iter) { | |
| 566 if (iter->second->num_parameters() == arg_count) { | |
| 567 method = iter->second.get(); | |
| 568 break; | |
| 569 } | |
| 570 } | |
| 571 if (!method) { | |
| 572 return false; | |
| 573 } | |
| 574 | |
| 575 // Coerce | |
| 576 std::vector<jvalue> parameters(arg_count); | |
| 577 for (size_t i = 0; i < arg_count; ++i) { | |
| 578 parameters[i] = CoerceJavaScriptValueToJavaValue(args[i], | |
|
joth
2011/11/11 11:40:38
nit: if this is single thread and we keep the scop
| |
| 579 method->parameter_type(i)); | |
| 580 } | |
| 581 | |
| 582 // Call | |
| 583 *result = CallJNIMethod(java_object_.obj(), method->return_type(), | |
| 584 method->id(), ¶meters[0]); | |
| 585 return true; | |
| 586 } | |
| 587 | |
| 588 void JavaBoundObject::EnsureMethodsAreSetUp() { | |
| 589 if (are_methods_set_up_) { | |
| 590 return; | |
| 591 } | |
| 592 are_methods_set_up_ = true; | |
| 593 | |
| 594 JNIEnv* env = AttachCurrentThread(); | |
| 595 ScopedJavaLocalRef<jclass> clazz(env, static_cast<jclass>( | |
| 596 env->CallObjectMethod(java_object_.obj(), GetObjectGetClassID()))); | |
| 597 ScopedJavaLocalRef<jobjectArray> methods(env, static_cast<jobjectArray>( | |
| 598 env->CallObjectMethod(clazz.obj(), ClassGetMethodsID()))); | |
| 599 size_t num_methods = env->GetArrayLength(methods.obj()); | |
| 600 for (size_t i = 0; i < num_methods; ++i) { | |
| 601 ScopedJavaLocalRef<jobject> java_method( | |
| 602 env, | |
| 603 env->GetObjectArrayElement(methods.obj(), i)); | |
| 604 JavaMethod* method = new JavaMethod(java_method); | |
| 605 methods_.insert(std::make_pair(method->name(), method)); | |
| 606 } | |
| 607 } | |
| OLD | NEW |