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