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