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