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/strings/string_number_conversions.h" 11 #include "base/strings/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/public/web/WebBindings.h" 16 17 using base::StringPrintf; 18 using base::android::AttachCurrentThread; 19 using base::android::ConvertUTF8ToJavaString; 20 using base::android::GetClass; 21 using base::android::GetMethodIDFromClassName; 22 using base::android::JavaRef; 23 using base::android::ScopedJavaGlobalRef; 24 using base::android::ScopedJavaLocalRef; 25 using 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 35 namespace content { 36 namespace { 37 38 const char kJavaLangClass[] = "java/lang/Class"; 39 const char kJavaLangObject[] = "java/lang/Object"; 40 const char kJavaLangReflectMethod[] = "java/lang/reflect/Method"; 41 const char kGetClass[] = "getClass"; 42 const char kGetMethods[] = "getMethods"; 43 const char kIsAnnotationPresent[] = "isAnnotationPresent"; 44 const char kReturningJavaLangClass[] = "()Ljava/lang/Class;"; 45 const char kReturningJavaLangReflectMethodArray[] = 46 "()[Ljava/lang/reflect/Method;"; 47 const 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. 53 struct 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 69 const 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 83 NPObject* JavaNPObject::Allocate(NPP npp, NPClass* np_class) { 84 JavaNPObject* obj = new JavaNPObject(); 85 return obj; 86 } 87 88 void JavaNPObject::Deallocate(NPObject* np_object) { 89 JavaNPObject* obj = reinterpret_cast<JavaNPObject*>(np_object); 90 delete obj->bound_object; 91 delete obj; 92 } 93 94 bool 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 100 bool 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 108 bool 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 115 bool 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. 128 bool 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 226 double RoundDoubleTowardsZero(const double& x) { 227 if (std::isnan(x)) { 228 return 0.0; 229 } 230 return x > 0.0 ? floor(x) : ceil(x); 231 } 232 233 // Rounds to jlong using Java's type conversion rules. 234 jlong RoundDoubleToLong(const double& x) { 235 double intermediate = RoundDoubleTowardsZero(x); 236 // The int64 limits can not be converted exactly to double values, so we 237 // compare to custom constants. kint64max is 2^63 - 1, but the spacing 238 // between double values in the the range 2^62 to 2^63 is 2^10. The cast is 239 // required to silence a spurious gcc warning for integer overflow. 240 const int64 limit = (GG_INT64_C(1) << 63) - static_cast<uint64>(1 << 10); 241 DCHECK(limit > 0); 242 const double kLargestDoubleLessThanInt64Max = limit; 243 const double kSmallestDoubleGreaterThanInt64Min = -limit; 244 if (intermediate > kLargestDoubleLessThanInt64Max) { 245 return kint64max; 246 } 247 if (intermediate < kSmallestDoubleGreaterThanInt64Min) { 248 return kint64min; 249 } 250 return static_cast<jlong>(intermediate); 251 } 252 253 // Rounds to jint using Java's type conversion rules. 254 jint RoundDoubleToInt(const double& x) { 255 double intermediate = RoundDoubleTowardsZero(x); 256 // The int32 limits cast exactly to double values. 257 intermediate = std::min(intermediate, static_cast<double>(kint32max)); 258 intermediate = std::max(intermediate, static_cast<double>(kint32min)); 259 return static_cast<jint>(intermediate); 260 } 261 262 jvalue CoerceJavaScriptNumberToJavaValue(const NPVariant& variant, 263 const JavaType& target_type, 264 bool coerce_to_string) { 265 // See http://jdk6.java.net/plugin2/liveconnect/#JS_NUMBER_VALUES. 266 267 // For conversion to numeric types, we need to replicate Java's type 268 // conversion rules. This requires that for integer values, we simply discard 269 // all but the lowest n buts, where n is the number of bits in the target 270 // type. For double values, the logic is more involved. 271 jvalue result; 272 DCHECK(variant.type == NPVariantType_Int32 || 273 variant.type == NPVariantType_Double); 274 bool is_double = variant.type == NPVariantType_Double; 275 switch (target_type.type) { 276 case JavaType::TypeByte: 277 result.b = is_double ? 278 static_cast<jbyte>(RoundDoubleToInt(NPVARIANT_TO_DOUBLE(variant))) : 279 static_cast<jbyte>(NPVARIANT_TO_INT32(variant)); 280 break; 281 case JavaType::TypeChar: 282 // LIVECONNECT_COMPLIANCE: Existing behavior is to convert double to 0. 283 // Spec requires converting doubles similarly to how we convert doubles to 284 // other numeric types. 285 result.c = is_double ? 0 : 286 static_cast<jchar>(NPVARIANT_TO_INT32(variant)); 287 break; 288 case JavaType::TypeShort: 289 result.s = is_double ? 290 static_cast<jshort>(RoundDoubleToInt(NPVARIANT_TO_DOUBLE(variant))) : 291 static_cast<jshort>(NPVARIANT_TO_INT32(variant)); 292 break; 293 case JavaType::TypeInt: 294 result.i = is_double ? RoundDoubleToInt(NPVARIANT_TO_DOUBLE(variant)) : 295 NPVARIANT_TO_INT32(variant); 296 break; 297 case JavaType::TypeLong: 298 result.j = is_double ? RoundDoubleToLong(NPVARIANT_TO_DOUBLE(variant)) : 299 NPVARIANT_TO_INT32(variant); 300 break; 301 case JavaType::TypeFloat: 302 result.f = is_double ? static_cast<jfloat>(NPVARIANT_TO_DOUBLE(variant)) : 303 NPVARIANT_TO_INT32(variant); 304 break; 305 case JavaType::TypeDouble: 306 result.d = is_double ? NPVARIANT_TO_DOUBLE(variant) : 307 NPVARIANT_TO_INT32(variant); 308 break; 309 case JavaType::TypeObject: 310 // LIVECONNECT_COMPLIANCE: Existing behavior is to convert to null. Spec 311 // requires handling object equivalents of primitive types. 312 result.l = NULL; 313 break; 314 case JavaType::TypeString: 315 result.l = coerce_to_string ? 316 ConvertUTF8ToJavaString( 317 AttachCurrentThread(), 318 is_double ? 319 base::StringPrintf("%.6lg", NPVARIANT_TO_DOUBLE(variant)) : 320 base::Int64ToString(NPVARIANT_TO_INT32(variant))).Release() : 321 NULL; 322 break; 323 case JavaType::TypeBoolean: 324 // LIVECONNECT_COMPLIANCE: Existing behavior is to convert to false. Spec 325 // requires converting to false for 0 or NaN, true otherwise. 326 result.z = JNI_FALSE; 327 break; 328 case JavaType::TypeArray: 329 // LIVECONNECT_COMPLIANCE: Existing behavior is to convert to null. Spec 330 // requires raising a JavaScript exception. 331 result.l = NULL; 332 break; 333 case JavaType::TypeVoid: 334 // Conversion to void must never happen. 335 NOTREACHED(); 336 break; 337 } 338 return result; 339 } 340 341 jvalue CoerceJavaScriptBooleanToJavaValue(const NPVariant& variant, 342 const JavaType& target_type, 343 bool coerce_to_string) { 344 // See http://jdk6.java.net/plugin2/liveconnect/#JS_BOOLEAN_VALUES. 345 DCHECK_EQ(NPVariantType_Bool, variant.type); 346 bool boolean_value = NPVARIANT_TO_BOOLEAN(variant); 347 jvalue result; 348 switch (target_type.type) { 349 case JavaType::TypeBoolean: 350 result.z = boolean_value ? JNI_TRUE : JNI_FALSE; 351 break; 352 case JavaType::TypeObject: 353 // LIVECONNECT_COMPLIANCE: Existing behavior is to convert to NULL. Spec 354 // requires handling java.lang.Boolean and java.lang.Object. 355 result.l = NULL; 356 break; 357 case JavaType::TypeString: 358 result.l = coerce_to_string ? 359 ConvertUTF8ToJavaString(AttachCurrentThread(), 360 boolean_value ? "true" : "false").Release() : 361 NULL; 362 break; 363 case JavaType::TypeByte: 364 case JavaType::TypeChar: 365 case JavaType::TypeShort: 366 case JavaType::TypeInt: 367 case JavaType::TypeLong: 368 case JavaType::TypeFloat: 369 case JavaType::TypeDouble: { 370 // LIVECONNECT_COMPLIANCE: Existing behavior is to convert to 0. Spec 371 // requires converting to 0 or 1. 372 jvalue null_value = {0}; 373 result = null_value; 374 break; 375 } 376 case JavaType::TypeArray: 377 // LIVECONNECT_COMPLIANCE: Existing behavior is to convert to NULL. Spec 378 // requires raising a JavaScript exception. 379 result.l = NULL; 380 break; 381 case JavaType::TypeVoid: 382 // Conversion to void must never happen. 383 NOTREACHED(); 384 break; 385 } 386 return result; 387 } 388 389 jvalue CoerceJavaScriptStringToJavaValue(const NPVariant& variant, 390 const JavaType& target_type) { 391 // See http://jdk6.java.net/plugin2/liveconnect/#JS_STRING_VALUES. 392 DCHECK_EQ(NPVariantType_String, variant.type); 393 jvalue result; 394 switch (target_type.type) { 395 case JavaType::TypeString: 396 result.l = ConvertUTF8ToJavaString( 397 AttachCurrentThread(), 398 base::StringPiece(NPVARIANT_TO_STRING(variant).UTF8Characters, 399 NPVARIANT_TO_STRING(variant).UTF8Length)).Release(); 400 break; 401 case JavaType::TypeObject: 402 // LIVECONNECT_COMPLIANCE: Existing behavior is to convert to NULL. Spec 403 // requires handling java.lang.Object. 404 result.l = NULL; 405 break; 406 case JavaType::TypeByte: 407 case JavaType::TypeShort: 408 case JavaType::TypeInt: 409 case JavaType::TypeLong: 410 case JavaType::TypeFloat: 411 case JavaType::TypeDouble: { 412 // LIVECONNECT_COMPLIANCE: Existing behavior is to convert to 0. Spec 413 // requires using valueOf() method of corresponding object type. 414 jvalue null_value = {0}; 415 result = null_value; 416 break; 417 } 418 case JavaType::TypeChar: 419 // LIVECONNECT_COMPLIANCE: Existing behavior is to convert to 0. Spec 420 // requires using java.lang.Short.decode(). 421 result.c = 0; 422 break; 423 case JavaType::TypeBoolean: 424 // LIVECONNECT_COMPLIANCE: Existing behavior is to convert to false. Spec 425 // requires converting the empty string to false, otherwise true. 426 result.z = JNI_FALSE; 427 break; 428 case JavaType::TypeArray: 429 // LIVECONNECT_COMPLIANCE: Existing behavior is to convert to NULL. Spec 430 // requires raising a JavaScript exception. 431 result.l = NULL; 432 break; 433 case JavaType::TypeVoid: 434 // Conversion to void must never happen. 435 NOTREACHED(); 436 break; 437 } 438 return result; 439 } 440 441 // Note that this only handles primitive types and strings. 442 jobject CreateJavaArray(const JavaType& type, jsize length) { 443 JNIEnv* env = AttachCurrentThread(); 444 switch (type.type) { 445 case JavaType::TypeBoolean: 446 return env->NewBooleanArray(length); 447 case JavaType::TypeByte: 448 return env->NewByteArray(length); 449 case JavaType::TypeChar: 450 return env->NewCharArray(length); 451 case JavaType::TypeShort: 452 return env->NewShortArray(length); 453 case JavaType::TypeInt: 454 return env->NewIntArray(length); 455 case JavaType::TypeLong: 456 return env->NewLongArray(length); 457 case JavaType::TypeFloat: 458 return env->NewFloatArray(length); 459 case JavaType::TypeDouble: 460 return env->NewDoubleArray(length); 461 case JavaType::TypeString: { 462 ScopedJavaLocalRef<jclass> clazz(GetClass(env, "java/lang/String")); 463 return env->NewObjectArray(length, clazz.obj(), NULL); 464 } 465 case JavaType::TypeVoid: 466 // Conversion to void must never happen. 467 case JavaType::TypeArray: 468 case JavaType::TypeObject: 469 // Not handled. 470 NOTREACHED(); 471 } 472 return NULL; 473 } 474 475 // Sets the specified element of the supplied array to the value of the 476 // supplied jvalue. Requires that the type of the array matches that of the 477 // jvalue. Handles only primitive types and strings. Note that in the case of a 478 // string, the array takes a new reference to the string object. 479 void SetArrayElement(jobject array, 480 const JavaType& type, 481 jsize index, 482 const jvalue& value) { 483 JNIEnv* env = AttachCurrentThread(); 484 switch (type.type) { 485 case JavaType::TypeBoolean: 486 env->SetBooleanArrayRegion(static_cast<jbooleanArray>(array), index, 1, 487 &value.z); 488 break; 489 case JavaType::TypeByte: 490 env->SetByteArrayRegion(static_cast<jbyteArray>(array), index, 1, 491 &value.b); 492 break; 493 case JavaType::TypeChar: 494 env->SetCharArrayRegion(static_cast<jcharArray>(array), index, 1, 495 &value.c); 496 break; 497 case JavaType::TypeShort: 498 env->SetShortArrayRegion(static_cast<jshortArray>(array), index, 1, 499 &value.s); 500 break; 501 case JavaType::TypeInt: 502 env->SetIntArrayRegion(static_cast<jintArray>(array), index, 1, 503 &value.i); 504 break; 505 case JavaType::TypeLong: 506 env->SetLongArrayRegion(static_cast<jlongArray>(array), index, 1, 507 &value.j); 508 break; 509 case JavaType::TypeFloat: 510 env->SetFloatArrayRegion(static_cast<jfloatArray>(array), index, 1, 511 &value.f); 512 break; 513 case JavaType::TypeDouble: 514 env->SetDoubleArrayRegion(static_cast<jdoubleArray>(array), index, 1, 515 &value.d); 516 break; 517 case JavaType::TypeString: 518 env->SetObjectArrayElement(static_cast<jobjectArray>(array), index, 519 value.l); 520 break; 521 case JavaType::TypeVoid: 522 // Conversion to void must never happen. 523 case JavaType::TypeArray: 524 case JavaType::TypeObject: 525 // Not handled. 526 NOTREACHED(); 527 } 528 base::android::CheckException(env); 529 } 530 531 void ReleaseJavaValueIfRequired(JNIEnv* env, 532 jvalue* value, 533 const JavaType& type) { 534 if (type.type == JavaType::TypeString || 535 type.type == JavaType::TypeObject || 536 type.type == JavaType::TypeArray) { 537 env->DeleteLocalRef(value->l); 538 value->l = NULL; 539 } 540 } 541 542 jvalue CoerceJavaScriptValueToJavaValue(const NPVariant& variant, 543 const JavaType& target_type, 544 bool coerce_to_string); 545 546 // Returns a new local reference to a Java array. 547 jobject CoerceJavaScriptObjectToArray(const NPVariant& variant, 548 const JavaType& target_type) { 549 DCHECK_EQ(JavaType::TypeArray, target_type.type); 550 NPObject* object = NPVARIANT_TO_OBJECT(variant); 551 DCHECK_NE(&JavaNPObject::kNPClass, object->_class); 552 553 const JavaType& target_inner_type = *target_type.inner_type.get(); 554 // LIVECONNECT_COMPLIANCE: Existing behavior is to return null for 555 // multi-dimensional arrays. Spec requires handling multi-demensional arrays. 556 if (target_inner_type.type == JavaType::TypeArray) { 557 return NULL; 558 } 559 560 // LIVECONNECT_COMPLIANCE: Existing behavior is to return null for object 561 // arrays. Spec requires handling object arrays. 562 if (target_inner_type.type == JavaType::TypeObject) { 563 return NULL; 564 } 565 566 // If the object does not have a length property, return null. 567 NPVariant length_variant; 568 if (!WebBindings::getProperty(0, object, 569 WebBindings::getStringIdentifier("length"), 570 &length_variant)) { 571 WebBindings::releaseVariantValue(&length_variant); 572 return NULL; 573 } 574 575 // If the length property does not have numeric type, or is outside the valid 576 // range for a Java array length, return null. 577 jsize length = -1; 578 if (NPVARIANT_IS_INT32(length_variant) 579 && NPVARIANT_TO_INT32(length_variant) >= 0) { 580 length = NPVARIANT_TO_INT32(length_variant); 581 } else if (NPVARIANT_IS_DOUBLE(length_variant) 582 && NPVARIANT_TO_DOUBLE(length_variant) >= 0.0 583 && NPVARIANT_TO_DOUBLE(length_variant) <= kint32max) { 584 length = static_cast<jsize>(NPVARIANT_TO_DOUBLE(length_variant)); 585 } 586 WebBindings::releaseVariantValue(&length_variant); 587 if (length == -1) { 588 return NULL; 589 } 590 591 // Create the Java array. 592 // TODO(steveblock): Handle failure to create the array. 593 jobject result = CreateJavaArray(target_inner_type, length); 594 NPVariant value_variant; 595 JNIEnv* env = AttachCurrentThread(); 596 for (jsize i = 0; i < length; ++i) { 597 // It seems that getProperty() will set the variant to type void on failure, 598 // but this doesn't seem to be documented, so do it explicitly here for 599 // safety. 600 VOID_TO_NPVARIANT(value_variant); 601 // If this fails, for example due to a missing element, we simply treat the 602 // value as JavaScript undefined. 603 WebBindings::getProperty(0, object, WebBindings::getIntIdentifier(i), 604 &value_variant); 605 jvalue element = CoerceJavaScriptValueToJavaValue(value_variant, 606 target_inner_type, 607 false); 608 SetArrayElement(result, target_inner_type, i, element); 609 // CoerceJavaScriptValueToJavaValue() creates new local references to 610 // strings, objects and arrays. Of these, only strings can occur here. 611 // SetArrayElement() causes the array to take its own reference to the 612 // string, so we can now release the local reference. 613 DCHECK_NE(JavaType::TypeObject, target_inner_type.type); 614 DCHECK_NE(JavaType::TypeArray, target_inner_type.type); 615 ReleaseJavaValueIfRequired(env, &element, target_inner_type); 616 WebBindings::releaseVariantValue(&value_variant); 617 } 618 619 return result; 620 } 621 622 jvalue CoerceJavaScriptObjectToJavaValue(const NPVariant& variant, 623 const JavaType& target_type, 624 bool coerce_to_string) { 625 // This covers both JavaScript objects (including arrays) and Java objects. 626 // See http://jdk6.java.net/plugin2/liveconnect/#JS_OTHER_OBJECTS, 627 // http://jdk6.java.net/plugin2/liveconnect/#JS_ARRAY_VALUES and 628 // http://jdk6.java.net/plugin2/liveconnect/#JS_JAVA_OBJECTS 629 DCHECK_EQ(NPVariantType_Object, variant.type); 630 631 NPObject* object = NPVARIANT_TO_OBJECT(variant); 632 bool is_java_object = &JavaNPObject::kNPClass == object->_class; 633 634 jvalue result; 635 switch (target_type.type) { 636 case JavaType::TypeObject: 637 if (is_java_object) { 638 // LIVECONNECT_COMPLIANCE: Existing behavior is to pass all Java 639 // objects. Spec requires passing only Java objects which are 640 // assignment-compatibile. 641 result.l = AttachCurrentThread()->NewLocalRef( 642 JavaBoundObject::GetJavaObject(object).obj()); 643 } else { 644 // LIVECONNECT_COMPLIANCE: Existing behavior is to pass null. Spec 645 // requires converting if the target type is 646 // netscape.javascript.JSObject, otherwise raising a JavaScript 647 // exception. 648 result.l = NULL; 649 } 650 break; 651 case JavaType::TypeString: 652 // LIVECONNECT_COMPLIANCE: Existing behavior is to convert to 653 // "undefined". Spec requires calling toString() on the Java object. 654 result.l = coerce_to_string ? 655 ConvertUTF8ToJavaString(AttachCurrentThread(), "undefined"). 656 Release() : 657 NULL; 658 break; 659 case JavaType::TypeByte: 660 case JavaType::TypeShort: 661 case JavaType::TypeInt: 662 case JavaType::TypeLong: 663 case JavaType::TypeFloat: 664 case JavaType::TypeDouble: 665 case JavaType::TypeChar: { 666 // LIVECONNECT_COMPLIANCE: Existing behavior is to convert to 0. Spec 667 // requires raising a JavaScript exception. 668 jvalue null_value = {0}; 669 result = null_value; 670 break; 671 } 672 case JavaType::TypeBoolean: 673 // LIVECONNECT_COMPLIANCE: Existing behavior is to convert to false. Spec 674 // requires raising a JavaScript exception. 675 result.z = JNI_FALSE; 676 break; 677 case JavaType::TypeArray: 678 if (is_java_object) { 679 // LIVECONNECT_COMPLIANCE: Existing behavior is to convert to NULL. Spec 680 // requires raising a JavaScript exception. 681 result.l = NULL; 682 } else { 683 result.l = CoerceJavaScriptObjectToArray(variant, target_type); 684 } 685 break; 686 case JavaType::TypeVoid: 687 // Conversion to void must never happen. 688 NOTREACHED(); 689 break; 690 } 691 return result; 692 } 693 694 jvalue CoerceJavaScriptNullOrUndefinedToJavaValue(const NPVariant& variant, 695 const JavaType& target_type, 696 bool coerce_to_string) { 697 // See http://jdk6.java.net/plugin2/liveconnect/#JS_NULL. 698 DCHECK(variant.type == NPVariantType_Null || 699 variant.type == NPVariantType_Void); 700 jvalue result; 701 switch (target_type.type) { 702 case JavaType::TypeObject: 703 result.l = NULL; 704 break; 705 case JavaType::TypeString: 706 // LIVECONNECT_COMPLIANCE: Existing behavior is to convert undefined to 707 // "undefined". Spec requires converting undefined to NULL. 708 result.l = (coerce_to_string && variant.type == NPVariantType_Void) ? 709 ConvertUTF8ToJavaString(AttachCurrentThread(), "undefined"). 710 Release() : 711 NULL; 712 break; 713 case JavaType::TypeByte: 714 case JavaType::TypeChar: 715 case JavaType::TypeShort: 716 case JavaType::TypeInt: 717 case JavaType::TypeLong: 718 case JavaType::TypeFloat: 719 case JavaType::TypeDouble: { 720 jvalue null_value = {0}; 721 result = null_value; 722 break; 723 } 724 case JavaType::TypeBoolean: 725 result.z = JNI_FALSE; 726 break; 727 case JavaType::TypeArray: 728 // LIVECONNECT_COMPLIANCE: Existing behavior is to convert to NULL. Spec 729 // requires raising a JavaScript exception. 730 result.l = NULL; 731 break; 732 case JavaType::TypeVoid: 733 // Conversion to void must never happen. 734 NOTREACHED(); 735 break; 736 } 737 return result; 738 } 739 740 // coerce_to_string means that we should try to coerce all JavaScript values to 741 // strings when required, rather than simply converting to NULL. This is used 742 // to maintain current behaviour, which differs slightly depending upon whether 743 // or not the coercion in question is for an array element. 744 // 745 // Note that the jvalue returned by this method may contain a new local 746 // reference to an object (string, object or array). This must be released by 747 // the caller. 748 jvalue CoerceJavaScriptValueToJavaValue(const NPVariant& variant, 749 const JavaType& target_type, 750 bool coerce_to_string) { 751 // Note that in all these conversions, the relevant field of the jvalue must 752 // always be explicitly set, as jvalue does not initialize its fields. 753 754 switch (variant.type) { 755 case NPVariantType_Int32: 756 case NPVariantType_Double: 757 return CoerceJavaScriptNumberToJavaValue(variant, target_type, 758 coerce_to_string); 759 case NPVariantType_Bool: 760 return CoerceJavaScriptBooleanToJavaValue(variant, target_type, 761 coerce_to_string); 762 case NPVariantType_String: 763 return CoerceJavaScriptStringToJavaValue(variant, target_type); 764 case NPVariantType_Object: 765 return CoerceJavaScriptObjectToJavaValue(variant, target_type, 766 coerce_to_string); 767 case NPVariantType_Null: 768 case NPVariantType_Void: 769 return CoerceJavaScriptNullOrUndefinedToJavaValue(variant, target_type, 770 coerce_to_string); 771 } 772 NOTREACHED(); 773 return jvalue(); 774 } 775 776 } // namespace 777 778 NPObject* JavaBoundObject::Create( 779 const JavaRef<jobject>& object, 780 const JavaRef<jclass>& safe_annotation_clazz, 781 const base::WeakPtr<JavaBridgeDispatcherHostManager>& manager) { 782 // The first argument (a plugin's instance handle) is passed through to the 783 // allocate function directly, and we don't use it, so it's ok to be 0. 784 // The object is created with a ref count of one. 785 NPObject* np_object = WebBindings::createObject(0, const_cast<NPClass*>( 786 &JavaNPObject::kNPClass)); 787 // The NPObject takes ownership of the JavaBoundObject. 788 reinterpret_cast<JavaNPObject*>(np_object)->bound_object = 789 new JavaBoundObject(object, safe_annotation_clazz, manager); 790 return np_object; 791 } 792 793 JavaBoundObject::JavaBoundObject( 794 const JavaRef<jobject>& object, 795 const JavaRef<jclass>& safe_annotation_clazz, 796 const base::WeakPtr<JavaBridgeDispatcherHostManager>& manager) 797 : java_object_(AttachCurrentThread(), object.obj()), 798 manager_(manager), 799 are_methods_set_up_(false), 800 safe_annotation_clazz_(safe_annotation_clazz) { 801 BrowserThread::PostTask( 802 BrowserThread::UI, FROM_HERE, 803 base::Bind(&JavaBridgeDispatcherHostManager::JavaBoundObjectCreated, 804 manager_, 805 base::android::ScopedJavaGlobalRef<jobject>(object))); 806 // Other than informing the JavaBridgeDispatcherHostManager that a java bound 807 // object has been created (above), we don't do anything else with our Java 808 // object when first created. We do it all lazily when a method is first 809 // invoked. 810 } 811 812 JavaBoundObject::~JavaBoundObject() { 813 BrowserThread::PostTask( 814 BrowserThread::UI, FROM_HERE, 815 base::Bind(&JavaBridgeDispatcherHostManager::JavaBoundObjectDestroyed, 816 manager_, 817 base::android::ScopedJavaGlobalRef<jobject>( 818 java_object_.get(AttachCurrentThread())))); 819 } 820 821 ScopedJavaLocalRef<jobject> JavaBoundObject::GetJavaObject(NPObject* object) { 822 DCHECK_EQ(&JavaNPObject::kNPClass, object->_class); 823 JavaBoundObject* jbo = reinterpret_cast<JavaNPObject*>(object)->bound_object; 824 return jbo->java_object_.get(AttachCurrentThread()); 825 } 826 827 bool JavaBoundObject::HasMethod(const std::string& name) const { 828 EnsureMethodsAreSetUp(); 829 return methods_.find(name) != methods_.end(); 830 } 831 832 bool JavaBoundObject::Invoke(const std::string& name, const NPVariant* args, 833 size_t arg_count, NPVariant* result) { 834 EnsureMethodsAreSetUp(); 835 836 // Get all methods with the correct name. 837 std::pair<JavaMethodMap::const_iterator, JavaMethodMap::const_iterator> 838 iters = methods_.equal_range(name); 839 if (iters.first == iters.second) { 840 return false; 841 } 842 843 // Take the first method with the correct number of arguments. 844 JavaMethod* method = NULL; 845 for (JavaMethodMap::const_iterator iter = iters.first; iter != iters.second; 846 ++iter) { 847 if (iter->second->num_parameters() == arg_count) { 848 method = iter->second.get(); 849 break; 850 } 851 } 852 if (!method) { 853 return false; 854 } 855 856 // Coerce 857 std::vector<jvalue> parameters(arg_count); 858 for (size_t i = 0; i < arg_count; ++i) { 859 parameters[i] = CoerceJavaScriptValueToJavaValue(args[i], 860 method->parameter_type(i), 861 true); 862 } 863 864 ScopedJavaLocalRef<jobject> obj = java_object_.get(AttachCurrentThread()); 865 866 bool ok = false; 867 if (!obj.is_null()) { 868 // Call 869 ok = CallJNIMethod(obj.obj(), method->return_type(), 870 method->id(), ¶meters[0], result, 871 safe_annotation_clazz_, 872 manager_); 873 } 874 875 // Now that we're done with the jvalue, release any local references created 876 // by CoerceJavaScriptValueToJavaValue(). 877 JNIEnv* env = AttachCurrentThread(); 878 for (size_t i = 0; i < arg_count; ++i) { 879 ReleaseJavaValueIfRequired(env, ¶meters[i], method->parameter_type(i)); 880 } 881 882 return ok; 883 } 884 885 void JavaBoundObject::EnsureMethodsAreSetUp() const { 886 if (are_methods_set_up_) 887 return; 888 are_methods_set_up_ = true; 889 890 JNIEnv* env = AttachCurrentThread(); 891 ScopedJavaLocalRef<jobject> obj = java_object_.get(env); 892 893 if (obj.is_null()) { 894 return; 895 } 896 897 ScopedJavaLocalRef<jclass> clazz(env, static_cast<jclass>( 898 env->CallObjectMethod(obj.obj(), GetMethodIDFromClassName( 899 env, 900 kJavaLangObject, 901 kGetClass, 902 kReturningJavaLangClass)))); 903 904 ScopedJavaLocalRef<jobjectArray> methods(env, static_cast<jobjectArray>( 905 env->CallObjectMethod(clazz.obj(), GetMethodIDFromClassName( 906 env, 907 kJavaLangClass, 908 kGetMethods, 909 kReturningJavaLangReflectMethodArray)))); 910 911 size_t num_methods = env->GetArrayLength(methods.obj()); 912 // Java objects always have public methods. 913 DCHECK(num_methods); 914 915 for (size_t i = 0; i < num_methods; ++i) { 916 ScopedJavaLocalRef<jobject> java_method( 917 env, 918 env->GetObjectArrayElement(methods.obj(), i)); 919 920 if (!safe_annotation_clazz_.is_null()) { 921 jboolean safe = env->CallBooleanMethod(java_method.obj(), 922 GetMethodIDFromClassName( 923 env, 924 kJavaLangReflectMethod, 925 kIsAnnotationPresent, 926 kTakesJavaLangClassReturningBoolean), 927 safe_annotation_clazz_.obj()); 928 929 if (!safe) 930 continue; 931 } 932 933 JavaMethod* method = new JavaMethod(java_method); 934 methods_.insert(std::make_pair(method->name(), method)); 935 } 936 } 937 938 } // namespace content 939