java_bound_object.cc revision 2a99a7e74a7f215066514fe81d2bfa6639d9eddd
1// Copyright (c) 2012 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 "content/browser/renderer_host/java/java_bridge_dispatcher_host_manager.h" 13#include "content/browser/renderer_host/java/java_type.h" 14#include "content/public/browser/browser_thread.h" 15#include "third_party/WebKit/Source/WebKit/chromium/public/WebBindings.h" 16 17using base::StringPrintf; 18using base::android::AttachCurrentThread; 19using base::android::ConvertUTF8ToJavaString; 20using base::android::GetClass; 21using base::android::GetMethodIDFromClassName; 22using base::android::JavaRef; 23using base::android::ScopedJavaGlobalRef; 24using base::android::ScopedJavaLocalRef; 25using WebKit::WebBindings; 26 27// The conversion between JavaScript and Java types is based on the Live 28// Connect 2 spec. See 29// http://jdk6.java.net/plugin2/liveconnect/#JS_JAVA_CONVERSIONS. 30 31// Note that in some cases, we differ from from the spec in order to maintain 32// existing behavior. These areas are marked LIVECONNECT_COMPLIANCE. We may 33// revisit this decision in the future. 34 35namespace content { 36namespace { 37 38const char kJavaLangClass[] = "java/lang/Class"; 39const char kJavaLangObject[] = "java/lang/Object"; 40const char kJavaLangReflectMethod[] = "java/lang/reflect/Method"; 41const char kGetClass[] = "getClass"; 42const char kGetMethods[] = "getMethods"; 43const char kIsAnnotationPresent[] = "isAnnotationPresent"; 44const char kReturningJavaLangClass[] = "()Ljava/lang/Class;"; 45const char kReturningJavaLangReflectMethodArray[] = 46 "()[Ljava/lang/reflect/Method;"; 47const char kTakesJavaLangClassReturningBoolean[] = "(Ljava/lang/Class;)Z"; 48 49// Our special NPObject type. We extend an NPObject with a pointer to a 50// JavaBoundObject. We also add static methods for each of the NPObject 51// callbacks, which are registered by our NPClass. These methods simply 52// delegate to the private implementation methods of JavaBoundObject. 53struct JavaNPObject : public NPObject { 54 JavaBoundObject* bound_object; 55 56 static const NPClass kNPClass; 57 58 static NPObject* Allocate(NPP npp, NPClass* np_class); 59 static void Deallocate(NPObject* np_object); 60 static bool HasMethod(NPObject* np_object, NPIdentifier np_identifier); 61 static bool Invoke(NPObject* np_object, NPIdentifier np_identifier, 62 const NPVariant *args, uint32_t arg_count, 63 NPVariant *result); 64 static bool HasProperty(NPObject* np_object, NPIdentifier np_identifier); 65 static bool GetProperty(NPObject* np_object, NPIdentifier np_identifier, 66 NPVariant *result); 67}; 68 69const NPClass JavaNPObject::kNPClass = { 70 NP_CLASS_STRUCT_VERSION, 71 JavaNPObject::Allocate, 72 JavaNPObject::Deallocate, 73 NULL, // NPInvalidate 74 JavaNPObject::HasMethod, 75 JavaNPObject::Invoke, 76 NULL, // NPInvokeDefault 77 JavaNPObject::HasProperty, 78 JavaNPObject::GetProperty, 79 NULL, // NPSetProperty, 80 NULL, // NPRemoveProperty 81}; 82 83NPObject* JavaNPObject::Allocate(NPP npp, NPClass* np_class) { 84 JavaNPObject* obj = new JavaNPObject(); 85 return obj; 86} 87 88void JavaNPObject::Deallocate(NPObject* np_object) { 89 JavaNPObject* obj = reinterpret_cast<JavaNPObject*>(np_object); 90 delete obj->bound_object; 91 delete obj; 92} 93 94bool JavaNPObject::HasMethod(NPObject* np_object, NPIdentifier np_identifier) { 95 std::string name(WebBindings::utf8FromIdentifier(np_identifier)); 96 JavaNPObject* obj = reinterpret_cast<JavaNPObject*>(np_object); 97 return obj->bound_object->HasMethod(name); 98} 99 100bool JavaNPObject::Invoke(NPObject* np_object, NPIdentifier np_identifier, 101 const NPVariant* args, uint32_t arg_count, 102 NPVariant* result) { 103 std::string name(WebBindings::utf8FromIdentifier(np_identifier)); 104 JavaNPObject* obj = reinterpret_cast<JavaNPObject*>(np_object); 105 return obj->bound_object->Invoke(name, args, arg_count, result); 106} 107 108bool JavaNPObject::HasProperty(NPObject* np_object, 109 NPIdentifier np_identifier) { 110 // LIVECONNECT_COMPLIANCE: Existing behavior is to return false to indicate 111 // that the property is not present. Spec requires supporting this correctly. 112 return false; 113} 114 115bool JavaNPObject::GetProperty(NPObject* np_object, 116 NPIdentifier np_identifier, 117 NPVariant* result) { 118 // LIVECONNECT_COMPLIANCE: Existing behavior is to return false to indicate 119 // that the property is undefined. Spec requires supporting this correctly. 120 return false; 121} 122 123// Calls a Java method through JNI. If the Java method raises an uncaught 124// exception, it is cleared and this method returns false. Otherwise, this 125// method returns true and the Java method's return value is provided as an 126// NPVariant. Note that this method does not do any type coercion. The Java 127// return value is simply converted to the corresponding NPAPI type. 128bool CallJNIMethod( 129 jobject object, 130 const JavaType& return_type, 131 jmethodID id, 132 jvalue* parameters, 133 NPVariant* result, 134 const JavaRef<jclass>& safe_annotation_clazz, 135 const base::WeakPtr<JavaBridgeDispatcherHostManager>& manager) { 136 JNIEnv* env = AttachCurrentThread(); 137 switch (return_type.type) { 138 case JavaType::TypeBoolean: 139 BOOLEAN_TO_NPVARIANT(env->CallBooleanMethodA(object, id, parameters), 140 *result); 141 break; 142 case JavaType::TypeByte: 143 INT32_TO_NPVARIANT(env->CallByteMethodA(object, id, parameters), *result); 144 break; 145 case JavaType::TypeChar: 146 INT32_TO_NPVARIANT(env->CallCharMethodA(object, id, parameters), *result); 147 break; 148 case JavaType::TypeShort: 149 INT32_TO_NPVARIANT(env->CallShortMethodA(object, id, parameters), 150 *result); 151 break; 152 case JavaType::TypeInt: 153 INT32_TO_NPVARIANT(env->CallIntMethodA(object, id, parameters), *result); 154 break; 155 case JavaType::TypeLong: 156 DOUBLE_TO_NPVARIANT(env->CallLongMethodA(object, id, parameters), 157 *result); 158 break; 159 case JavaType::TypeFloat: 160 DOUBLE_TO_NPVARIANT(env->CallFloatMethodA(object, id, parameters), 161 *result); 162 break; 163 case JavaType::TypeDouble: 164 DOUBLE_TO_NPVARIANT(env->CallDoubleMethodA(object, id, parameters), 165 *result); 166 break; 167 case JavaType::TypeVoid: 168 env->CallVoidMethodA(object, id, parameters); 169 VOID_TO_NPVARIANT(*result); 170 break; 171 case JavaType::TypeArray: 172 // LIVECONNECT_COMPLIANCE: Existing behavior is to not call methods that 173 // return arrays. Spec requires calling the method and converting the 174 // result to a JavaScript array. 175 VOID_TO_NPVARIANT(*result); 176 break; 177 case JavaType::TypeString: { 178 jstring java_string = static_cast<jstring>( 179 env->CallObjectMethodA(object, id, parameters)); 180 // If an exception was raised, we must clear it before calling most JNI 181 // methods. ScopedJavaLocalRef is liable to make such calls, so we test 182 // first. 183 if (base::android::ClearException(env)) { 184 return false; 185 } 186 ScopedJavaLocalRef<jstring> scoped_java_string(env, java_string); 187 if (!scoped_java_string.obj()) { 188 // LIVECONNECT_COMPLIANCE: Existing behavior is to return undefined. 189 // Spec requires returning a null string. 190 VOID_TO_NPVARIANT(*result); 191 break; 192 } 193 std::string str = 194 base::android::ConvertJavaStringToUTF8(scoped_java_string); 195 size_t length = str.length(); 196 // This pointer is freed in _NPN_ReleaseVariantValue in 197 // third_party/WebKit/Source/WebCore/bindings/v8/npruntime.cpp. 198 char* buffer = static_cast<char*>(malloc(length)); 199 str.copy(buffer, length, 0); 200 STRINGN_TO_NPVARIANT(buffer, length, *result); 201 break; 202 } 203 case JavaType::TypeObject: { 204 // If an exception was raised, we must clear it before calling most JNI 205 // methods. ScopedJavaLocalRef is liable to make such calls, so we test 206 // first. 207 jobject java_object = env->CallObjectMethodA(object, id, parameters); 208 if (base::android::ClearException(env)) { 209 return false; 210 } 211 ScopedJavaLocalRef<jobject> scoped_java_object(env, java_object); 212 if (!scoped_java_object.obj()) { 213 NULL_TO_NPVARIANT(*result); 214 break; 215 } 216 OBJECT_TO_NPVARIANT(JavaBoundObject::Create(scoped_java_object, 217 safe_annotation_clazz, 218 manager), 219 *result); 220 break; 221 } 222 } 223 return !base::android::ClearException(env); 224} 225 226jvalue CoerceJavaScriptNumberToJavaValue(const NPVariant& variant, 227 const JavaType& target_type, 228 bool coerce_to_string) { 229 // See http://jdk6.java.net/plugin2/liveconnect/#JS_NUMBER_VALUES. 230 jvalue result; 231 DCHECK(variant.type == NPVariantType_Int32 || 232 variant.type == NPVariantType_Double); 233 bool is_double = variant.type == NPVariantType_Double; 234 switch (target_type.type) { 235 case JavaType::TypeByte: 236 result.b = is_double ? static_cast<jbyte>(NPVARIANT_TO_DOUBLE(variant)) : 237 static_cast<jbyte>(NPVARIANT_TO_INT32(variant)); 238 break; 239 case JavaType::TypeChar: 240 // LIVECONNECT_COMPLIANCE: Existing behavior is to convert double to 0. 241 // Spec requires converting doubles the same as int32. 242 result.c = is_double ? 0 : 243 static_cast<jchar>(NPVARIANT_TO_INT32(variant)); 244 break; 245 case JavaType::TypeShort: 246 result.s = is_double ? static_cast<jshort>(NPVARIANT_TO_DOUBLE(variant)) : 247 static_cast<jshort>(NPVARIANT_TO_INT32(variant)); 248 break; 249 case JavaType::TypeInt: 250 result.i = is_double ? static_cast<jint>(NPVARIANT_TO_DOUBLE(variant)) : 251 NPVARIANT_TO_INT32(variant); 252 break; 253 case JavaType::TypeLong: 254 result.j = is_double ? static_cast<jlong>(NPVARIANT_TO_DOUBLE(variant)) : 255 NPVARIANT_TO_INT32(variant); 256 break; 257 case JavaType::TypeFloat: 258 result.f = is_double ? static_cast<jfloat>(NPVARIANT_TO_DOUBLE(variant)) : 259 NPVARIANT_TO_INT32(variant); 260 break; 261 case JavaType::TypeDouble: 262 result.d = is_double ? NPVARIANT_TO_DOUBLE(variant) : 263 NPVARIANT_TO_INT32(variant); 264 break; 265 case JavaType::TypeObject: 266 // LIVECONNECT_COMPLIANCE: Existing behavior is to convert to null. Spec 267 // requires handling object equivalents of primitive types. 268 result.l = NULL; 269 break; 270 case JavaType::TypeString: 271 result.l = coerce_to_string ? 272 ConvertUTF8ToJavaString( 273 AttachCurrentThread(), 274 is_double ? 275 base::StringPrintf("%.6lg", NPVARIANT_TO_DOUBLE(variant)) : 276 base::Int64ToString(NPVARIANT_TO_INT32(variant))).Release() : 277 NULL; 278 break; 279 case JavaType::TypeBoolean: 280 // LIVECONNECT_COMPLIANCE: Existing behavior is to convert to false. Spec 281 // requires converting to false for 0 or NaN, true otherwise. 282 result.z = JNI_FALSE; 283 break; 284 case JavaType::TypeArray: 285 // LIVECONNECT_COMPLIANCE: Existing behavior is to convert to null. Spec 286 // requires raising a JavaScript exception. 287 result.l = NULL; 288 break; 289 case JavaType::TypeVoid: 290 // Conversion to void must never happen. 291 NOTREACHED(); 292 break; 293 } 294 return result; 295} 296 297jvalue CoerceJavaScriptBooleanToJavaValue(const NPVariant& variant, 298 const JavaType& target_type, 299 bool coerce_to_string) { 300 // See http://jdk6.java.net/plugin2/liveconnect/#JS_BOOLEAN_VALUES. 301 DCHECK_EQ(NPVariantType_Bool, variant.type); 302 bool boolean_value = NPVARIANT_TO_BOOLEAN(variant); 303 jvalue result; 304 switch (target_type.type) { 305 case JavaType::TypeBoolean: 306 result.z = boolean_value ? JNI_TRUE : JNI_FALSE; 307 break; 308 case JavaType::TypeObject: 309 // LIVECONNECT_COMPLIANCE: Existing behavior is to convert to NULL. Spec 310 // requires handling java.lang.Boolean and java.lang.Object. 311 result.l = NULL; 312 break; 313 case JavaType::TypeString: 314 result.l = coerce_to_string ? 315 ConvertUTF8ToJavaString(AttachCurrentThread(), 316 boolean_value ? "true" : "false").Release() : 317 NULL; 318 break; 319 case JavaType::TypeByte: 320 case JavaType::TypeChar: 321 case JavaType::TypeShort: 322 case JavaType::TypeInt: 323 case JavaType::TypeLong: 324 case JavaType::TypeFloat: 325 case JavaType::TypeDouble: { 326 // LIVECONNECT_COMPLIANCE: Existing behavior is to convert to 0. Spec 327 // requires converting to 0 or 1. 328 jvalue null_value = {0}; 329 result = null_value; 330 break; 331 } 332 case JavaType::TypeArray: 333 // LIVECONNECT_COMPLIANCE: Existing behavior is to convert to NULL. Spec 334 // requires raising a JavaScript exception. 335 result.l = NULL; 336 break; 337 case JavaType::TypeVoid: 338 // Conversion to void must never happen. 339 NOTREACHED(); 340 break; 341 } 342 return result; 343} 344 345jvalue CoerceJavaScriptStringToJavaValue(const NPVariant& variant, 346 const JavaType& target_type) { 347 // See http://jdk6.java.net/plugin2/liveconnect/#JS_STRING_VALUES. 348 DCHECK_EQ(NPVariantType_String, variant.type); 349 jvalue result; 350 switch (target_type.type) { 351 case JavaType::TypeString: 352 result.l = ConvertUTF8ToJavaString( 353 AttachCurrentThread(), 354 base::StringPiece(NPVARIANT_TO_STRING(variant).UTF8Characters, 355 NPVARIANT_TO_STRING(variant).UTF8Length)).Release(); 356 break; 357 case JavaType::TypeObject: 358 // LIVECONNECT_COMPLIANCE: Existing behavior is to convert to NULL. Spec 359 // requires handling java.lang.Object. 360 result.l = NULL; 361 break; 362 case JavaType::TypeByte: 363 case JavaType::TypeShort: 364 case JavaType::TypeInt: 365 case JavaType::TypeLong: 366 case JavaType::TypeFloat: 367 case JavaType::TypeDouble: { 368 // LIVECONNECT_COMPLIANCE: Existing behavior is to convert to 0. Spec 369 // requires using valueOf() method of corresponding object type. 370 jvalue null_value = {0}; 371 result = null_value; 372 break; 373 } 374 case JavaType::TypeChar: 375 // LIVECONNECT_COMPLIANCE: Existing behavior is to convert to 0. Spec 376 // requires using java.lang.Short.decode(). 377 result.c = 0; 378 break; 379 case JavaType::TypeBoolean: 380 // LIVECONNECT_COMPLIANCE: Existing behavior is to convert to false. Spec 381 // requires converting the empty string to false, otherwise true. 382 result.z = JNI_FALSE; 383 break; 384 case JavaType::TypeArray: 385 // LIVECONNECT_COMPLIANCE: Existing behavior is to convert to NULL. Spec 386 // requires raising a JavaScript exception. 387 result.l = NULL; 388 break; 389 case JavaType::TypeVoid: 390 // Conversion to void must never happen. 391 NOTREACHED(); 392 break; 393 } 394 return result; 395} 396 397// Note that this only handles primitive types and strings. 398jobject CreateJavaArray(const JavaType& type, jsize length) { 399 JNIEnv* env = AttachCurrentThread(); 400 switch (type.type) { 401 case JavaType::TypeBoolean: 402 return env->NewBooleanArray(length); 403 case JavaType::TypeByte: 404 return env->NewByteArray(length); 405 case JavaType::TypeChar: 406 return env->NewCharArray(length); 407 case JavaType::TypeShort: 408 return env->NewShortArray(length); 409 case JavaType::TypeInt: 410 return env->NewIntArray(length); 411 case JavaType::TypeLong: 412 return env->NewLongArray(length); 413 case JavaType::TypeFloat: 414 return env->NewFloatArray(length); 415 case JavaType::TypeDouble: 416 return env->NewDoubleArray(length); 417 case JavaType::TypeString: { 418 ScopedJavaLocalRef<jclass> clazz(GetClass(env, "java/lang/String")); 419 return env->NewObjectArray(length, clazz.obj(), NULL); 420 } 421 case JavaType::TypeVoid: 422 // Conversion to void must never happen. 423 case JavaType::TypeArray: 424 case JavaType::TypeObject: 425 // Not handled. 426 NOTREACHED(); 427 } 428 return NULL; 429} 430 431// Sets the specified element of the supplied array to the value of the 432// supplied jvalue. Requires that the type of the array matches that of the 433// jvalue. Handles only primitive types and strings. Note that in the case of a 434// string, the array takes a new reference to the string object. 435void SetArrayElement(jobject array, 436 const JavaType& type, 437 jsize index, 438 const jvalue& value) { 439 JNIEnv* env = AttachCurrentThread(); 440 switch (type.type) { 441 case JavaType::TypeBoolean: 442 env->SetBooleanArrayRegion(static_cast<jbooleanArray>(array), index, 1, 443 &value.z); 444 break; 445 case JavaType::TypeByte: 446 env->SetByteArrayRegion(static_cast<jbyteArray>(array), index, 1, 447 &value.b); 448 break; 449 case JavaType::TypeChar: 450 env->SetCharArrayRegion(static_cast<jcharArray>(array), index, 1, 451 &value.c); 452 break; 453 case JavaType::TypeShort: 454 env->SetShortArrayRegion(static_cast<jshortArray>(array), index, 1, 455 &value.s); 456 break; 457 case JavaType::TypeInt: 458 env->SetIntArrayRegion(static_cast<jintArray>(array), index, 1, 459 &value.i); 460 break; 461 case JavaType::TypeLong: 462 env->SetLongArrayRegion(static_cast<jlongArray>(array), index, 1, 463 &value.j); 464 break; 465 case JavaType::TypeFloat: 466 env->SetFloatArrayRegion(static_cast<jfloatArray>(array), index, 1, 467 &value.f); 468 break; 469 case JavaType::TypeDouble: 470 env->SetDoubleArrayRegion(static_cast<jdoubleArray>(array), index, 1, 471 &value.d); 472 break; 473 case JavaType::TypeString: 474 env->SetObjectArrayElement(static_cast<jobjectArray>(array), index, 475 value.l); 476 break; 477 case JavaType::TypeVoid: 478 // Conversion to void must never happen. 479 case JavaType::TypeArray: 480 case JavaType::TypeObject: 481 // Not handled. 482 NOTREACHED(); 483 } 484 base::android::CheckException(env); 485} 486 487void ReleaseJavaValueIfRequired(JNIEnv* env, 488 jvalue* value, 489 const JavaType& type) { 490 if (type.type == JavaType::TypeString || 491 type.type == JavaType::TypeObject || 492 type.type == JavaType::TypeArray) { 493 env->DeleteLocalRef(value->l); 494 value->l = NULL; 495 } 496} 497 498jvalue CoerceJavaScriptValueToJavaValue(const NPVariant& variant, 499 const JavaType& target_type, 500 bool coerce_to_string); 501 502// Returns a new local reference to a Java array. 503jobject CoerceJavaScriptObjectToArray(const NPVariant& variant, 504 const JavaType& target_type) { 505 DCHECK_EQ(JavaType::TypeArray, target_type.type); 506 NPObject* object = NPVARIANT_TO_OBJECT(variant); 507 DCHECK_NE(&JavaNPObject::kNPClass, object->_class); 508 509 const JavaType& target_inner_type = *target_type.inner_type.get(); 510 // LIVECONNECT_COMPLIANCE: Existing behavior is to return null for 511 // multi-dimensional arrays. Spec requires handling multi-demensional arrays. 512 if (target_inner_type.type == JavaType::TypeArray) { 513 return NULL; 514 } 515 516 // LIVECONNECT_COMPLIANCE: Existing behavior is to return null for object 517 // arrays. Spec requires handling object arrays. 518 if (target_inner_type.type == JavaType::TypeObject) { 519 return NULL; 520 } 521 522 // If the object does not have a length property, return null. 523 NPVariant length_variant; 524 if (!WebBindings::getProperty(0, object, 525 WebBindings::getStringIdentifier("length"), 526 &length_variant)) { 527 WebBindings::releaseVariantValue(&length_variant); 528 return NULL; 529 } 530 531 // If the length property does not have numeric type, or is outside the valid 532 // range for a Java array length, return null. 533 jsize length = -1; 534 if (NPVARIANT_IS_INT32(length_variant) 535 && NPVARIANT_TO_INT32(length_variant) >= 0) { 536 length = NPVARIANT_TO_INT32(length_variant); 537 } else if (NPVARIANT_IS_DOUBLE(length_variant) 538 && NPVARIANT_TO_DOUBLE(length_variant) >= 0.0 539 && NPVARIANT_TO_DOUBLE(length_variant) <= kint32max) { 540 length = static_cast<jsize>(NPVARIANT_TO_DOUBLE(length_variant)); 541 } 542 WebBindings::releaseVariantValue(&length_variant); 543 if (length == -1) { 544 return NULL; 545 } 546 547 // Create the Java array. 548 // TODO(steveblock): Handle failure to create the array. 549 jobject result = CreateJavaArray(target_inner_type, length); 550 NPVariant value_variant; 551 JNIEnv* env = AttachCurrentThread(); 552 for (jsize i = 0; i < length; ++i) { 553 // It seems that getProperty() will set the variant to type void on failure, 554 // but this doesn't seem to be documented, so do it explicitly here for 555 // safety. 556 VOID_TO_NPVARIANT(value_variant); 557 // If this fails, for example due to a missing element, we simply treat the 558 // value as JavaScript undefined. 559 WebBindings::getProperty(0, object, WebBindings::getIntIdentifier(i), 560 &value_variant); 561 jvalue element = CoerceJavaScriptValueToJavaValue(value_variant, 562 target_inner_type, 563 false); 564 SetArrayElement(result, target_inner_type, i, element); 565 // CoerceJavaScriptValueToJavaValue() creates new local references to 566 // strings, objects and arrays. Of these, only strings can occur here. 567 // SetArrayElement() causes the array to take its own reference to the 568 // string, so we can now release the local reference. 569 DCHECK_NE(JavaType::TypeObject, target_inner_type.type); 570 DCHECK_NE(JavaType::TypeArray, target_inner_type.type); 571 ReleaseJavaValueIfRequired(env, &element, target_inner_type); 572 WebBindings::releaseVariantValue(&value_variant); 573 } 574 575 return result; 576} 577 578jvalue CoerceJavaScriptObjectToJavaValue(const NPVariant& variant, 579 const JavaType& target_type, 580 bool coerce_to_string) { 581 // This covers both JavaScript objects (including arrays) and Java objects. 582 // See http://jdk6.java.net/plugin2/liveconnect/#JS_OTHER_OBJECTS, 583 // http://jdk6.java.net/plugin2/liveconnect/#JS_ARRAY_VALUES and 584 // http://jdk6.java.net/plugin2/liveconnect/#JS_JAVA_OBJECTS 585 DCHECK_EQ(NPVariantType_Object, variant.type); 586 587 NPObject* object = NPVARIANT_TO_OBJECT(variant); 588 bool is_java_object = &JavaNPObject::kNPClass == object->_class; 589 590 jvalue result; 591 switch (target_type.type) { 592 case JavaType::TypeObject: 593 if (is_java_object) { 594 // LIVECONNECT_COMPLIANCE: Existing behavior is to pass all Java 595 // objects. Spec requires passing only Java objects which are 596 // assignment-compatibile. 597 result.l = AttachCurrentThread()->NewLocalRef( 598 JavaBoundObject::GetJavaObject(object).obj()); 599 } else { 600 // LIVECONNECT_COMPLIANCE: Existing behavior is to pass null. Spec 601 // requires converting if the target type is 602 // netscape.javascript.JSObject, otherwise raising a JavaScript 603 // exception. 604 result.l = NULL; 605 } 606 break; 607 case JavaType::TypeString: 608 // LIVECONNECT_COMPLIANCE: Existing behavior is to convert to 609 // "undefined". Spec requires calling toString() on the Java object. 610 result.l = coerce_to_string ? 611 ConvertUTF8ToJavaString(AttachCurrentThread(), "undefined"). 612 Release() : 613 NULL; 614 break; 615 case JavaType::TypeByte: 616 case JavaType::TypeShort: 617 case JavaType::TypeInt: 618 case JavaType::TypeLong: 619 case JavaType::TypeFloat: 620 case JavaType::TypeDouble: 621 case JavaType::TypeChar: { 622 // LIVECONNECT_COMPLIANCE: Existing behavior is to convert to 0. Spec 623 // requires raising a JavaScript exception. 624 jvalue null_value = {0}; 625 result = null_value; 626 break; 627 } 628 case JavaType::TypeBoolean: 629 // LIVECONNECT_COMPLIANCE: Existing behavior is to convert to false. Spec 630 // requires raising a JavaScript exception. 631 result.z = JNI_FALSE; 632 break; 633 case JavaType::TypeArray: 634 if (is_java_object) { 635 // LIVECONNECT_COMPLIANCE: Existing behavior is to convert to NULL. Spec 636 // requires raising a JavaScript exception. 637 result.l = NULL; 638 } else { 639 result.l = CoerceJavaScriptObjectToArray(variant, target_type); 640 } 641 break; 642 case JavaType::TypeVoid: 643 // Conversion to void must never happen. 644 NOTREACHED(); 645 break; 646 } 647 return result; 648} 649 650jvalue CoerceJavaScriptNullOrUndefinedToJavaValue(const NPVariant& variant, 651 const JavaType& target_type, 652 bool coerce_to_string) { 653 // See http://jdk6.java.net/plugin2/liveconnect/#JS_NULL. 654 DCHECK(variant.type == NPVariantType_Null || 655 variant.type == NPVariantType_Void); 656 jvalue result; 657 switch (target_type.type) { 658 case JavaType::TypeObject: 659 result.l = NULL; 660 break; 661 case JavaType::TypeString: 662 // LIVECONNECT_COMPLIANCE: Existing behavior is to convert undefined to 663 // "undefined". Spec requires converting undefined to NULL. 664 result.l = (coerce_to_string && variant.type == NPVariantType_Void) ? 665 ConvertUTF8ToJavaString(AttachCurrentThread(), "undefined"). 666 Release() : 667 NULL; 668 break; 669 case JavaType::TypeByte: 670 case JavaType::TypeChar: 671 case JavaType::TypeShort: 672 case JavaType::TypeInt: 673 case JavaType::TypeLong: 674 case JavaType::TypeFloat: 675 case JavaType::TypeDouble: { 676 jvalue null_value = {0}; 677 result = null_value; 678 break; 679 } 680 case JavaType::TypeBoolean: 681 result.z = JNI_FALSE; 682 break; 683 case JavaType::TypeArray: 684 // LIVECONNECT_COMPLIANCE: Existing behavior is to convert to NULL. Spec 685 // requires raising a JavaScript exception. 686 result.l = NULL; 687 break; 688 case JavaType::TypeVoid: 689 // Conversion to void must never happen. 690 NOTREACHED(); 691 break; 692 } 693 return result; 694} 695 696// coerce_to_string means that we should try to coerce all JavaScript values to 697// strings when required, rather than simply converting to NULL. This is used 698// to maintain current behaviour, which differs slightly depending upon whether 699// or not the coercion in question is for an array element. 700// 701// Note that the jvalue returned by this method may contain a new local 702// reference to an object (string, object or array). This must be released by 703// the caller. 704jvalue CoerceJavaScriptValueToJavaValue(const NPVariant& variant, 705 const JavaType& target_type, 706 bool coerce_to_string) { 707 // Note that in all these conversions, the relevant field of the jvalue must 708 // always be explicitly set, as jvalue does not initialize its fields. 709 710 switch (variant.type) { 711 case NPVariantType_Int32: 712 case NPVariantType_Double: 713 return CoerceJavaScriptNumberToJavaValue(variant, target_type, 714 coerce_to_string); 715 case NPVariantType_Bool: 716 return CoerceJavaScriptBooleanToJavaValue(variant, target_type, 717 coerce_to_string); 718 case NPVariantType_String: 719 return CoerceJavaScriptStringToJavaValue(variant, target_type); 720 case NPVariantType_Object: 721 return CoerceJavaScriptObjectToJavaValue(variant, target_type, 722 coerce_to_string); 723 case NPVariantType_Null: 724 case NPVariantType_Void: 725 return CoerceJavaScriptNullOrUndefinedToJavaValue(variant, target_type, 726 coerce_to_string); 727 } 728 NOTREACHED(); 729 return jvalue(); 730} 731 732} // namespace 733 734NPObject* JavaBoundObject::Create( 735 const JavaRef<jobject>& object, 736 const JavaRef<jclass>& safe_annotation_clazz, 737 const base::WeakPtr<JavaBridgeDispatcherHostManager>& manager) { 738 // The first argument (a plugin's instance handle) is passed through to the 739 // allocate function directly, and we don't use it, so it's ok to be 0. 740 // The object is created with a ref count of one. 741 NPObject* np_object = WebBindings::createObject(0, const_cast<NPClass*>( 742 &JavaNPObject::kNPClass)); 743 // The NPObject takes ownership of the JavaBoundObject. 744 reinterpret_cast<JavaNPObject*>(np_object)->bound_object = 745 new JavaBoundObject(object, safe_annotation_clazz, manager); 746 return np_object; 747} 748 749JavaBoundObject::JavaBoundObject( 750 const JavaRef<jobject>& object, 751 const JavaRef<jclass>& safe_annotation_clazz, 752 const base::WeakPtr<JavaBridgeDispatcherHostManager>& manager) 753 : java_object_(AttachCurrentThread(), object.obj()), 754 manager_(manager), 755 are_methods_set_up_(false), 756 safe_annotation_clazz_(safe_annotation_clazz) { 757 BrowserThread::PostTask( 758 BrowserThread::UI, FROM_HERE, 759 base::Bind(&JavaBridgeDispatcherHostManager::JavaBoundObjectCreated, 760 manager_, 761 base::android::ScopedJavaGlobalRef<jobject>(object))); 762 // Other than informing the JavaBridgeDispatcherHostManager that a java bound 763 // object has been created (above), we don't do anything else with our Java 764 // object when first created. We do it all lazily when a method is first 765 // invoked. 766} 767 768JavaBoundObject::~JavaBoundObject() { 769 BrowserThread::PostTask( 770 BrowserThread::UI, FROM_HERE, 771 base::Bind(&JavaBridgeDispatcherHostManager::JavaBoundObjectDestroyed, 772 manager_, 773 base::android::ScopedJavaGlobalRef<jobject>( 774 java_object_.get(AttachCurrentThread())))); 775} 776 777ScopedJavaLocalRef<jobject> JavaBoundObject::GetJavaObject(NPObject* object) { 778 DCHECK_EQ(&JavaNPObject::kNPClass, object->_class); 779 JavaBoundObject* jbo = reinterpret_cast<JavaNPObject*>(object)->bound_object; 780 return jbo->java_object_.get(AttachCurrentThread()); 781} 782 783bool JavaBoundObject::HasMethod(const std::string& name) const { 784 EnsureMethodsAreSetUp(); 785 return methods_.find(name) != methods_.end(); 786} 787 788bool JavaBoundObject::Invoke(const std::string& name, const NPVariant* args, 789 size_t arg_count, NPVariant* result) { 790 EnsureMethodsAreSetUp(); 791 792 // Get all methods with the correct name. 793 std::pair<JavaMethodMap::const_iterator, JavaMethodMap::const_iterator> 794 iters = methods_.equal_range(name); 795 if (iters.first == iters.second) { 796 return false; 797 } 798 799 // Take the first method with the correct number of arguments. 800 JavaMethod* method = NULL; 801 for (JavaMethodMap::const_iterator iter = iters.first; iter != iters.second; 802 ++iter) { 803 if (iter->second->num_parameters() == arg_count) { 804 method = iter->second.get(); 805 break; 806 } 807 } 808 if (!method) { 809 return false; 810 } 811 812 // Coerce 813 std::vector<jvalue> parameters(arg_count); 814 for (size_t i = 0; i < arg_count; ++i) { 815 parameters[i] = CoerceJavaScriptValueToJavaValue(args[i], 816 method->parameter_type(i), 817 true); 818 } 819 820 ScopedJavaLocalRef<jobject> obj = java_object_.get(AttachCurrentThread()); 821 822 bool ok = false; 823 if (!obj.is_null()) { 824 // Call 825 ok = CallJNIMethod(obj.obj(), method->return_type(), 826 method->id(), ¶meters[0], result, 827 safe_annotation_clazz_, 828 manager_); 829 } 830 831 // Now that we're done with the jvalue, release any local references created 832 // by CoerceJavaScriptValueToJavaValue(). 833 JNIEnv* env = AttachCurrentThread(); 834 for (size_t i = 0; i < arg_count; ++i) { 835 ReleaseJavaValueIfRequired(env, ¶meters[i], method->parameter_type(i)); 836 } 837 838 return ok; 839} 840 841void JavaBoundObject::EnsureMethodsAreSetUp() const { 842 if (are_methods_set_up_) 843 return; 844 are_methods_set_up_ = true; 845 846 JNIEnv* env = AttachCurrentThread(); 847 ScopedJavaLocalRef<jobject> obj = java_object_.get(env); 848 849 if (obj.is_null()) { 850 return; 851 } 852 853 ScopedJavaLocalRef<jclass> clazz(env, static_cast<jclass>( 854 env->CallObjectMethod(obj.obj(), GetMethodIDFromClassName( 855 env, 856 kJavaLangObject, 857 kGetClass, 858 kReturningJavaLangClass)))); 859 860 ScopedJavaLocalRef<jobjectArray> methods(env, static_cast<jobjectArray>( 861 env->CallObjectMethod(clazz.obj(), GetMethodIDFromClassName( 862 env, 863 kJavaLangClass, 864 kGetMethods, 865 kReturningJavaLangReflectMethodArray)))); 866 867 size_t num_methods = env->GetArrayLength(methods.obj()); 868 // Java objects always have public methods. 869 DCHECK(num_methods); 870 871 for (size_t i = 0; i < num_methods; ++i) { 872 ScopedJavaLocalRef<jobject> java_method( 873 env, 874 env->GetObjectArrayElement(methods.obj(), i)); 875 876 if (!safe_annotation_clazz_.is_null()) { 877 jboolean safe = env->CallBooleanMethod(java_method.obj(), 878 GetMethodIDFromClassName( 879 env, 880 kJavaLangReflectMethod, 881 kIsAnnotationPresent, 882 kTakesJavaLangClassReturningBoolean), 883 safe_annotation_clazz_.obj()); 884 885 if (!safe) 886 continue; 887 } 888 889 JavaMethod* method = new JavaMethod(java_method); 890 methods_.insert(std::make_pair(method->name(), method)); 891 } 892} 893 894} // namespace content 895