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.
5 #include "content/browser/renderer_host/java/java_bound_object.h"
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/renderer_host/java/java_bridge_dispatcher_host_manager.h"
14 #include "content/browser/renderer_host/java/java_type.h"
15 #include "content/browser/renderer_host/java/jni_helper.h"
16 #include "content/public/browser/browser_thread.h"
17 #include "third_party/WebKit/public/web/WebBindings.h"
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;
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.
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.
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";
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;
61 static const NPClass kNPClass;
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,
69 static bool HasProperty(NPObject* np_object, NPIdentifier np_identifier);
70 static bool GetProperty(NPObject* np_object, NPIdentifier np_identifier,
72 static bool Enumerate(NPObject* object, NPIdentifier** values,
76 const NPClass JavaNPObject::kNPClass = {
77 NP_CLASS_STRUCT_VERSION,
78 JavaNPObject::Allocate,
79 JavaNPObject::Deallocate,
81 JavaNPObject::HasMethod,
83 NULL, // NPInvokeDefault
84 JavaNPObject::HasProperty,
85 JavaNPObject::GetProperty,
86 NULL, // NPSetProperty,
87 NULL, // NPRemoveProperty
88 JavaNPObject::Enumerate,
92 NPObject* JavaNPObject::Allocate(NPP npp, NPClass* np_class) {
93 JavaNPObject* obj = new JavaNPObject();
97 void JavaNPObject::Deallocate(NPObject* np_object) {
98 JavaNPObject* obj = reinterpret_cast<JavaNPObject*>(np_object);
99 delete obj->bound_object;
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);
109 bool JavaNPObject::Invoke(NPObject* np_object, NPIdentifier np_identifier,
110 const NPVariant* args, uint32_t arg_count,
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);
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.
124 bool JavaNPObject::GetProperty(NPObject* np_object,
125 NPIdentifier np_identifier,
127 // LIVECONNECT_COMPLIANCE: Existing behavior is to return false to indicate
128 // that the property is undefined. Spec requires supporting this correctly.
132 bool JavaNPObject::Enumerate(NPObject* np_object, NPIdentifier** values,
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());
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.
152 const JavaType& return_type,
156 const JavaRef<jclass>& safe_annotation_clazz,
157 const base::WeakPtr<JavaBridgeDispatcherHostManager>& manager,
158 bool can_enumerate_methods) {
159 JNIEnv* env = AttachCurrentThread();
160 switch (return_type.type) {
161 case JavaType::TypeBoolean:
162 BOOLEAN_TO_NPVARIANT(env->CallBooleanMethodA(object, id, parameters),
165 case JavaType::TypeByte:
166 INT32_TO_NPVARIANT(env->CallByteMethodA(object, id, parameters), *result);
168 case JavaType::TypeChar:
169 INT32_TO_NPVARIANT(env->CallCharMethodA(object, id, parameters), *result);
171 case JavaType::TypeShort:
172 INT32_TO_NPVARIANT(env->CallShortMethodA(object, id, parameters),
175 case JavaType::TypeInt:
176 INT32_TO_NPVARIANT(env->CallIntMethodA(object, id, parameters), *result);
178 case JavaType::TypeLong:
179 DOUBLE_TO_NPVARIANT(env->CallLongMethodA(object, id, parameters),
182 case JavaType::TypeFloat:
183 DOUBLE_TO_NPVARIANT(env->CallFloatMethodA(object, id, parameters),
186 case JavaType::TypeDouble:
187 DOUBLE_TO_NPVARIANT(env->CallDoubleMethodA(object, id, parameters),
190 case JavaType::TypeVoid:
191 env->CallVoidMethodA(object, id, parameters);
192 VOID_TO_NPVARIANT(*result);
194 case JavaType::TypeArray:
195 // LIVECONNECT_COMPLIANCE: Existing behavior is to not call methods that
196 // return arrays. Spec requires calling the method and converting the
197 // result to a JavaScript array.
198 VOID_TO_NPVARIANT(*result);
200 case JavaType::TypeString: {
201 jstring java_string = static_cast<jstring>(
202 env->CallObjectMethodA(object, id, parameters));
203 // If an exception was raised, we must clear it before calling most JNI
204 // methods. ScopedJavaLocalRef is liable to make such calls, so we test
206 if (base::android::ClearException(env)) {
209 ScopedJavaLocalRef<jstring> scoped_java_string(env, java_string);
210 if (!scoped_java_string.obj()) {
211 // LIVECONNECT_COMPLIANCE: Existing behavior is to return undefined.
212 // Spec requires returning a null string.
213 VOID_TO_NPVARIANT(*result);
217 base::android::ConvertJavaStringToUTF8(scoped_java_string);
218 size_t length = str.length();
219 // This pointer is freed in _NPN_ReleaseVariantValue in
220 // third_party/WebKit/Source/WebCore/bindings/v8/npruntime.cpp.
221 char* buffer = static_cast<char*>(malloc(length));
222 str.copy(buffer, length, 0);
223 STRINGN_TO_NPVARIANT(buffer, length, *result);
226 case JavaType::TypeObject: {
227 // If an exception was raised, we must clear it before calling most JNI
228 // methods. ScopedJavaLocalRef is liable to make such calls, so we test
230 jobject java_object = env->CallObjectMethodA(object, id, parameters);
231 if (base::android::ClearException(env)) {
234 ScopedJavaLocalRef<jobject> scoped_java_object(env, java_object);
235 if (!scoped_java_object.obj()) {
236 NULL_TO_NPVARIANT(*result);
239 OBJECT_TO_NPVARIANT(JavaBoundObject::Create(scoped_java_object,
240 safe_annotation_clazz,
242 can_enumerate_methods),
247 return !base::android::ClearException(env);
250 double RoundDoubleTowardsZero(const double& x) {
254 return x > 0.0 ? floor(x) : ceil(x);
257 // Rounds to jlong using Java's type conversion rules.
258 jlong RoundDoubleToLong(const double& x) {
259 double intermediate = RoundDoubleTowardsZero(x);
260 // The int64 limits can not be converted exactly to double values, so we
261 // compare to custom constants. kint64max is 2^63 - 1, but the spacing
262 // between double values in the the range 2^62 to 2^63 is 2^10. The cast is
263 // required to silence a spurious gcc warning for integer overflow.
264 const int64 limit = (GG_INT64_C(1) << 63) - static_cast<uint64>(1 << 10);
266 const double kLargestDoubleLessThanInt64Max = limit;
267 const double kSmallestDoubleGreaterThanInt64Min = -limit;
268 if (intermediate > kLargestDoubleLessThanInt64Max) {
271 if (intermediate < kSmallestDoubleGreaterThanInt64Min) {
274 return static_cast<jlong>(intermediate);
277 // Rounds to jint using Java's type conversion rules.
278 jint RoundDoubleToInt(const double& x) {
279 double intermediate = RoundDoubleTowardsZero(x);
280 // The int32 limits cast exactly to double values.
281 intermediate = std::min(intermediate, static_cast<double>(kint32max));
282 intermediate = std::max(intermediate, static_cast<double>(kint32min));
283 return static_cast<jint>(intermediate);
286 jvalue CoerceJavaScriptNumberToJavaValue(const NPVariant& variant,
287 const JavaType& target_type,
288 bool coerce_to_string) {
289 // See http://jdk6.java.net/plugin2/liveconnect/#JS_NUMBER_VALUES.
291 // For conversion to numeric types, we need to replicate Java's type
292 // conversion rules. This requires that for integer values, we simply discard
293 // all but the lowest n buts, where n is the number of bits in the target
294 // type. For double values, the logic is more involved.
296 DCHECK(variant.type == NPVariantType_Int32 ||
297 variant.type == NPVariantType_Double);
298 bool is_double = variant.type == NPVariantType_Double;
299 switch (target_type.type) {
300 case JavaType::TypeByte:
301 result.b = is_double ?
302 static_cast<jbyte>(RoundDoubleToInt(NPVARIANT_TO_DOUBLE(variant))) :
303 static_cast<jbyte>(NPVARIANT_TO_INT32(variant));
305 case JavaType::TypeChar:
306 // LIVECONNECT_COMPLIANCE: Existing behavior is to convert double to 0.
307 // Spec requires converting doubles similarly to how we convert doubles to
308 // other numeric types.
309 result.c = is_double ? 0 :
310 static_cast<jchar>(NPVARIANT_TO_INT32(variant));
312 case JavaType::TypeShort:
313 result.s = is_double ?
314 static_cast<jshort>(RoundDoubleToInt(NPVARIANT_TO_DOUBLE(variant))) :
315 static_cast<jshort>(NPVARIANT_TO_INT32(variant));
317 case JavaType::TypeInt:
318 result.i = is_double ? RoundDoubleToInt(NPVARIANT_TO_DOUBLE(variant)) :
319 NPVARIANT_TO_INT32(variant);
321 case JavaType::TypeLong:
322 result.j = is_double ? RoundDoubleToLong(NPVARIANT_TO_DOUBLE(variant)) :
323 NPVARIANT_TO_INT32(variant);
325 case JavaType::TypeFloat:
326 result.f = is_double ? static_cast<jfloat>(NPVARIANT_TO_DOUBLE(variant)) :
327 NPVARIANT_TO_INT32(variant);
329 case JavaType::TypeDouble:
330 result.d = is_double ? NPVARIANT_TO_DOUBLE(variant) :
331 NPVARIANT_TO_INT32(variant);
333 case JavaType::TypeObject:
334 // LIVECONNECT_COMPLIANCE: Existing behavior is to convert to null. Spec
335 // requires handling object equivalents of primitive types.
338 case JavaType::TypeString:
339 result.l = coerce_to_string ?
340 ConvertUTF8ToJavaString(
341 AttachCurrentThread(),
343 base::StringPrintf("%.6lg", NPVARIANT_TO_DOUBLE(variant)) :
344 base::Int64ToString(NPVARIANT_TO_INT32(variant))).Release() :
347 case JavaType::TypeBoolean:
348 // LIVECONNECT_COMPLIANCE: Existing behavior is to convert to false. Spec
349 // requires converting to false for 0 or NaN, true otherwise.
350 result.z = JNI_FALSE;
352 case JavaType::TypeArray:
353 // LIVECONNECT_COMPLIANCE: Existing behavior is to convert to null. Spec
354 // requires raising a JavaScript exception.
357 case JavaType::TypeVoid:
358 // Conversion to void must never happen.
365 jvalue CoerceJavaScriptBooleanToJavaValue(const NPVariant& variant,
366 const JavaType& target_type,
367 bool coerce_to_string) {
368 // See http://jdk6.java.net/plugin2/liveconnect/#JS_BOOLEAN_VALUES.
369 DCHECK_EQ(NPVariantType_Bool, variant.type);
370 bool boolean_value = NPVARIANT_TO_BOOLEAN(variant);
372 switch (target_type.type) {
373 case JavaType::TypeBoolean:
374 result.z = boolean_value ? JNI_TRUE : JNI_FALSE;
376 case JavaType::TypeObject:
377 // LIVECONNECT_COMPLIANCE: Existing behavior is to convert to NULL. Spec
378 // requires handling java.lang.Boolean and java.lang.Object.
381 case JavaType::TypeString:
382 result.l = coerce_to_string ?
383 ConvertUTF8ToJavaString(AttachCurrentThread(),
384 boolean_value ? "true" : "false").Release() :
387 case JavaType::TypeByte:
388 case JavaType::TypeChar:
389 case JavaType::TypeShort:
390 case JavaType::TypeInt:
391 case JavaType::TypeLong:
392 case JavaType::TypeFloat:
393 case JavaType::TypeDouble: {
394 // LIVECONNECT_COMPLIANCE: Existing behavior is to convert to 0. Spec
395 // requires converting to 0 or 1.
396 jvalue null_value = {0};
400 case JavaType::TypeArray:
401 // LIVECONNECT_COMPLIANCE: Existing behavior is to convert to NULL. Spec
402 // requires raising a JavaScript exception.
405 case JavaType::TypeVoid:
406 // Conversion to void must never happen.
413 jvalue CoerceJavaScriptStringToJavaValue(const NPVariant& variant,
414 const JavaType& target_type) {
415 // See http://jdk6.java.net/plugin2/liveconnect/#JS_STRING_VALUES.
416 DCHECK_EQ(NPVariantType_String, variant.type);
418 switch (target_type.type) {
419 case JavaType::TypeString:
420 result.l = ConvertUTF8ToJavaString(
421 AttachCurrentThread(),
422 base::StringPiece(NPVARIANT_TO_STRING(variant).UTF8Characters,
423 NPVARIANT_TO_STRING(variant).UTF8Length)).Release();
425 case JavaType::TypeObject:
426 // LIVECONNECT_COMPLIANCE: Existing behavior is to convert to NULL. Spec
427 // requires handling java.lang.Object.
430 case JavaType::TypeByte:
431 case JavaType::TypeShort:
432 case JavaType::TypeInt:
433 case JavaType::TypeLong:
434 case JavaType::TypeFloat:
435 case JavaType::TypeDouble: {
436 // LIVECONNECT_COMPLIANCE: Existing behavior is to convert to 0. Spec
437 // requires using valueOf() method of corresponding object type.
438 jvalue null_value = {0};
442 case JavaType::TypeChar:
443 // LIVECONNECT_COMPLIANCE: Existing behavior is to convert to 0. Spec
444 // requires using java.lang.Short.decode().
447 case JavaType::TypeBoolean:
448 // LIVECONNECT_COMPLIANCE: Existing behavior is to convert to false. Spec
449 // requires converting the empty string to false, otherwise true.
450 result.z = JNI_FALSE;
452 case JavaType::TypeArray:
453 // LIVECONNECT_COMPLIANCE: Existing behavior is to convert to NULL. Spec
454 // requires raising a JavaScript exception.
457 case JavaType::TypeVoid:
458 // Conversion to void must never happen.
465 // Note that this only handles primitive types and strings.
466 jobject CreateJavaArray(const JavaType& type, jsize length) {
467 JNIEnv* env = AttachCurrentThread();
469 case JavaType::TypeBoolean:
470 return env->NewBooleanArray(length);
471 case JavaType::TypeByte:
472 return env->NewByteArray(length);
473 case JavaType::TypeChar:
474 return env->NewCharArray(length);
475 case JavaType::TypeShort:
476 return env->NewShortArray(length);
477 case JavaType::TypeInt:
478 return env->NewIntArray(length);
479 case JavaType::TypeLong:
480 return env->NewLongArray(length);
481 case JavaType::TypeFloat:
482 return env->NewFloatArray(length);
483 case JavaType::TypeDouble:
484 return env->NewDoubleArray(length);
485 case JavaType::TypeString: {
486 ScopedJavaLocalRef<jclass> clazz(GetClass(env, "java/lang/String"));
487 return env->NewObjectArray(length, clazz.obj(), NULL);
489 case JavaType::TypeVoid:
490 // Conversion to void must never happen.
491 case JavaType::TypeArray:
492 case JavaType::TypeObject:
499 // Sets the specified element of the supplied array to the value of the
500 // supplied jvalue. Requires that the type of the array matches that of the
501 // jvalue. Handles only primitive types and strings. Note that in the case of a
502 // string, the array takes a new reference to the string object.
503 void SetArrayElement(jobject array,
504 const JavaType& type,
506 const jvalue& value) {
507 JNIEnv* env = AttachCurrentThread();
509 case JavaType::TypeBoolean:
510 env->SetBooleanArrayRegion(static_cast<jbooleanArray>(array), index, 1,
513 case JavaType::TypeByte:
514 env->SetByteArrayRegion(static_cast<jbyteArray>(array), index, 1,
517 case JavaType::TypeChar:
518 env->SetCharArrayRegion(static_cast<jcharArray>(array), index, 1,
521 case JavaType::TypeShort:
522 env->SetShortArrayRegion(static_cast<jshortArray>(array), index, 1,
525 case JavaType::TypeInt:
526 env->SetIntArrayRegion(static_cast<jintArray>(array), index, 1,
529 case JavaType::TypeLong:
530 env->SetLongArrayRegion(static_cast<jlongArray>(array), index, 1,
533 case JavaType::TypeFloat:
534 env->SetFloatArrayRegion(static_cast<jfloatArray>(array), index, 1,
537 case JavaType::TypeDouble:
538 env->SetDoubleArrayRegion(static_cast<jdoubleArray>(array), index, 1,
541 case JavaType::TypeString:
542 env->SetObjectArrayElement(static_cast<jobjectArray>(array), index,
545 case JavaType::TypeVoid:
546 // Conversion to void must never happen.
547 case JavaType::TypeArray:
548 case JavaType::TypeObject:
552 base::android::CheckException(env);
555 void ReleaseJavaValueIfRequired(JNIEnv* env,
557 const JavaType& type) {
558 if (type.type == JavaType::TypeString ||
559 type.type == JavaType::TypeObject ||
560 type.type == JavaType::TypeArray) {
561 env->DeleteLocalRef(value->l);
566 jvalue CoerceJavaScriptValueToJavaValue(const NPVariant& variant,
567 const JavaType& target_type,
568 bool coerce_to_string);
570 // Returns a new local reference to a Java array.
571 jobject CoerceJavaScriptObjectToArray(const NPVariant& variant,
572 const JavaType& target_type) {
573 DCHECK_EQ(JavaType::TypeArray, target_type.type);
574 NPObject* object = NPVARIANT_TO_OBJECT(variant);
575 DCHECK_NE(&JavaNPObject::kNPClass, object->_class);
577 const JavaType& target_inner_type = *target_type.inner_type.get();
578 // LIVECONNECT_COMPLIANCE: Existing behavior is to return null for
579 // multi-dimensional arrays. Spec requires handling multi-demensional arrays.
580 if (target_inner_type.type == JavaType::TypeArray) {
584 // LIVECONNECT_COMPLIANCE: Existing behavior is to return null for object
585 // arrays. Spec requires handling object arrays.
586 if (target_inner_type.type == JavaType::TypeObject) {
590 // If the object does not have a length property, return null.
591 NPVariant length_variant;
592 if (!WebBindings::getProperty(0, object,
593 WebBindings::getStringIdentifier("length"),
595 WebBindings::releaseVariantValue(&length_variant);
599 // If the length property does not have numeric type, or is outside the valid
600 // range for a Java array length, return null.
602 if (NPVARIANT_IS_INT32(length_variant)
603 && NPVARIANT_TO_INT32(length_variant) >= 0) {
604 length = NPVARIANT_TO_INT32(length_variant);
605 } else if (NPVARIANT_IS_DOUBLE(length_variant)
606 && NPVARIANT_TO_DOUBLE(length_variant) >= 0.0
607 && NPVARIANT_TO_DOUBLE(length_variant) <= kint32max) {
608 length = static_cast<jsize>(NPVARIANT_TO_DOUBLE(length_variant));
610 WebBindings::releaseVariantValue(&length_variant);
615 // Create the Java array.
616 // TODO(steveblock): Handle failure to create the array.
617 jobject result = CreateJavaArray(target_inner_type, length);
618 NPVariant value_variant;
619 JNIEnv* env = AttachCurrentThread();
620 for (jsize i = 0; i < length; ++i) {
621 // It seems that getProperty() will set the variant to type void on failure,
622 // but this doesn't seem to be documented, so do it explicitly here for
624 VOID_TO_NPVARIANT(value_variant);
625 // If this fails, for example due to a missing element, we simply treat the
626 // value as JavaScript undefined.
627 WebBindings::getProperty(0, object, WebBindings::getIntIdentifier(i),
629 jvalue element = CoerceJavaScriptValueToJavaValue(value_variant,
632 SetArrayElement(result, target_inner_type, i, element);
633 // CoerceJavaScriptValueToJavaValue() creates new local references to
634 // strings, objects and arrays. Of these, only strings can occur here.
635 // SetArrayElement() causes the array to take its own reference to the
636 // string, so we can now release the local reference.
637 DCHECK_NE(JavaType::TypeObject, target_inner_type.type);
638 DCHECK_NE(JavaType::TypeArray, target_inner_type.type);
639 ReleaseJavaValueIfRequired(env, &element, target_inner_type);
640 WebBindings::releaseVariantValue(&value_variant);
646 jvalue CoerceJavaScriptObjectToJavaValue(const NPVariant& variant,
647 const JavaType& target_type,
648 bool coerce_to_string) {
649 // This covers both JavaScript objects (including arrays) and Java objects.
650 // See http://jdk6.java.net/plugin2/liveconnect/#JS_OTHER_OBJECTS,
651 // http://jdk6.java.net/plugin2/liveconnect/#JS_ARRAY_VALUES and
652 // http://jdk6.java.net/plugin2/liveconnect/#JS_JAVA_OBJECTS
653 DCHECK_EQ(NPVariantType_Object, variant.type);
655 NPObject* object = NPVARIANT_TO_OBJECT(variant);
656 bool is_java_object = &JavaNPObject::kNPClass == object->_class;
659 switch (target_type.type) {
660 case JavaType::TypeObject:
661 if (is_java_object) {
662 // LIVECONNECT_COMPLIANCE: Existing behavior is to pass all Java
663 // objects. Spec requires passing only Java objects which are
664 // assignment-compatibile.
665 result.l = AttachCurrentThread()->NewLocalRef(
666 JavaBoundObject::GetJavaObject(object).obj());
668 // LIVECONNECT_COMPLIANCE: Existing behavior is to pass null. Spec
669 // requires converting if the target type is
670 // netscape.javascript.JSObject, otherwise raising a JavaScript
675 case JavaType::TypeString:
676 // LIVECONNECT_COMPLIANCE: Existing behavior is to convert to
677 // "undefined". Spec requires calling toString() on the Java object.
678 result.l = coerce_to_string ?
679 ConvertUTF8ToJavaString(AttachCurrentThread(), "undefined").
683 case JavaType::TypeByte:
684 case JavaType::TypeShort:
685 case JavaType::TypeInt:
686 case JavaType::TypeLong:
687 case JavaType::TypeFloat:
688 case JavaType::TypeDouble:
689 case JavaType::TypeChar: {
690 // LIVECONNECT_COMPLIANCE: Existing behavior is to convert to 0. Spec
691 // requires raising a JavaScript exception.
692 jvalue null_value = {0};
696 case JavaType::TypeBoolean:
697 // LIVECONNECT_COMPLIANCE: Existing behavior is to convert to false. Spec
698 // requires raising a JavaScript exception.
699 result.z = JNI_FALSE;
701 case JavaType::TypeArray:
702 if (is_java_object) {
703 // LIVECONNECT_COMPLIANCE: Existing behavior is to convert to NULL. Spec
704 // requires raising a JavaScript exception.
707 result.l = CoerceJavaScriptObjectToArray(variant, target_type);
710 case JavaType::TypeVoid:
711 // Conversion to void must never happen.
718 jvalue CoerceJavaScriptNullOrUndefinedToJavaValue(const NPVariant& variant,
719 const JavaType& target_type,
720 bool coerce_to_string) {
721 // See http://jdk6.java.net/plugin2/liveconnect/#JS_NULL.
722 DCHECK(variant.type == NPVariantType_Null ||
723 variant.type == NPVariantType_Void);
725 switch (target_type.type) {
726 case JavaType::TypeObject:
729 case JavaType::TypeString:
730 // LIVECONNECT_COMPLIANCE: Existing behavior is to convert undefined to
731 // "undefined". Spec requires converting undefined to NULL.
732 result.l = (coerce_to_string && variant.type == NPVariantType_Void) ?
733 ConvertUTF8ToJavaString(AttachCurrentThread(), "undefined").
737 case JavaType::TypeByte:
738 case JavaType::TypeChar:
739 case JavaType::TypeShort:
740 case JavaType::TypeInt:
741 case JavaType::TypeLong:
742 case JavaType::TypeFloat:
743 case JavaType::TypeDouble: {
744 jvalue null_value = {0};
748 case JavaType::TypeBoolean:
749 result.z = JNI_FALSE;
751 case JavaType::TypeArray:
752 // LIVECONNECT_COMPLIANCE: Existing behavior is to convert to NULL. Spec
753 // requires raising a JavaScript exception.
756 case JavaType::TypeVoid:
757 // Conversion to void must never happen.
764 // coerce_to_string means that we should try to coerce all JavaScript values to
765 // strings when required, rather than simply converting to NULL. This is used
766 // to maintain current behaviour, which differs slightly depending upon whether
767 // or not the coercion in question is for an array element.
769 // Note that the jvalue returned by this method may contain a new local
770 // reference to an object (string, object or array). This must be released by
772 jvalue CoerceJavaScriptValueToJavaValue(const NPVariant& variant,
773 const JavaType& target_type,
774 bool coerce_to_string) {
775 // Note that in all these conversions, the relevant field of the jvalue must
776 // always be explicitly set, as jvalue does not initialize its fields.
778 switch (variant.type) {
779 case NPVariantType_Int32:
780 case NPVariantType_Double:
781 return CoerceJavaScriptNumberToJavaValue(variant, target_type,
783 case NPVariantType_Bool:
784 return CoerceJavaScriptBooleanToJavaValue(variant, target_type,
786 case NPVariantType_String:
787 return CoerceJavaScriptStringToJavaValue(variant, target_type);
788 case NPVariantType_Object:
789 return CoerceJavaScriptObjectToJavaValue(variant, target_type,
791 case NPVariantType_Null:
792 case NPVariantType_Void:
793 return CoerceJavaScriptNullOrUndefinedToJavaValue(variant, target_type,
802 NPObject* JavaBoundObject::Create(
803 const JavaRef<jobject>& object,
804 const JavaRef<jclass>& safe_annotation_clazz,
805 const base::WeakPtr<JavaBridgeDispatcherHostManager>& manager,
806 bool can_enumerate_methods) {
807 // The first argument (a plugin's instance handle) is passed through to the
808 // allocate function directly, and we don't use it, so it's ok to be 0.
809 // The object is created with a ref count of one.
810 NPObject* np_object = WebBindings::createObject(0, const_cast<NPClass*>(
811 &JavaNPObject::kNPClass));
812 // The NPObject takes ownership of the JavaBoundObject.
813 reinterpret_cast<JavaNPObject*>(np_object)->bound_object =
815 object, safe_annotation_clazz, manager, can_enumerate_methods);
819 JavaBoundObject::JavaBoundObject(
820 const JavaRef<jobject>& object,
821 const JavaRef<jclass>& safe_annotation_clazz,
822 const base::WeakPtr<JavaBridgeDispatcherHostManager>& manager,
823 bool can_enumerate_methods)
824 : java_object_(AttachCurrentThread(), object.obj()),
826 are_methods_set_up_(false),
827 object_get_class_method_id_(NULL),
828 can_enumerate_methods_(can_enumerate_methods),
829 safe_annotation_clazz_(safe_annotation_clazz) {
830 BrowserThread::PostTask(
831 BrowserThread::UI, FROM_HERE,
832 base::Bind(&JavaBridgeDispatcherHostManager::JavaBoundObjectCreated,
834 base::android::ScopedJavaGlobalRef<jobject>(object)));
835 // Other than informing the JavaBridgeDispatcherHostManager that a java bound
836 // object has been created (above), we don't do anything else with our Java
837 // object when first created. We do it all lazily when a method is first
841 JavaBoundObject::~JavaBoundObject() {
842 BrowserThread::PostTask(
843 BrowserThread::UI, FROM_HERE,
844 base::Bind(&JavaBridgeDispatcherHostManager::JavaBoundObjectDestroyed,
846 base::android::ScopedJavaGlobalRef<jobject>(
847 java_object_.get(AttachCurrentThread()))));
850 ScopedJavaLocalRef<jobject> JavaBoundObject::GetJavaObject(NPObject* object) {
851 DCHECK_EQ(&JavaNPObject::kNPClass, object->_class);
852 JavaBoundObject* jbo = reinterpret_cast<JavaNPObject*>(object)->bound_object;
853 return jbo->java_object_.get(AttachCurrentThread());
856 std::vector<std::string> JavaBoundObject::GetMethodNames() const {
857 EnsureMethodsAreSetUp();
858 std::vector<std::string> result;
859 for (JavaMethodMap::const_iterator it = methods_.begin();
860 it != methods_.end();
861 it = methods_.upper_bound(it->first)) {
862 result.push_back(it->first);
867 bool JavaBoundObject::HasMethod(const std::string& name) const {
868 EnsureMethodsAreSetUp();
869 return methods_.find(name) != methods_.end();
872 bool JavaBoundObject::Invoke(const std::string& name, const NPVariant* args,
873 size_t arg_count, NPVariant* result) {
874 EnsureMethodsAreSetUp();
876 // Get all methods with the correct name.
877 std::pair<JavaMethodMap::const_iterator, JavaMethodMap::const_iterator>
878 iters = methods_.equal_range(name);
879 if (iters.first == iters.second) {
883 // Take the first method with the correct number of arguments.
884 JavaMethod* method = NULL;
885 for (JavaMethodMap::const_iterator iter = iters.first; iter != iters.second;
887 if (iter->second->num_parameters() == arg_count) {
888 method = iter->second.get();
897 std::vector<jvalue> parameters(arg_count);
898 for (size_t i = 0; i < arg_count; ++i) {
899 parameters[i] = CoerceJavaScriptValueToJavaValue(args[i],
900 method->parameter_type(i),
904 JNIEnv* env = AttachCurrentThread();
905 ScopedJavaLocalRef<jobject> obj = java_object_.get(env);
907 // Block access to java.lang.Object.getClass.
908 // As it is declared to be final, it is sufficient to compare methodIDs.
909 if (method->id() == object_get_class_method_id_) {
910 BrowserThread::PostTask(
911 BrowserThread::UI, FROM_HERE,
912 base::Bind(&JavaBoundObject::ThrowSecurityException,
913 kAccessToObjectGetClassIsBlocked));
918 if (!obj.is_null()) {
920 ok = CallJNIMethod(obj.obj(), method->return_type(),
921 method->id(), ¶meters[0], result,
922 safe_annotation_clazz_,
924 can_enumerate_methods_);
927 // Now that we're done with the jvalue, release any local references created
928 // by CoerceJavaScriptValueToJavaValue().
929 for (size_t i = 0; i < arg_count; ++i) {
930 ReleaseJavaValueIfRequired(env, ¶meters[i], method->parameter_type(i));
936 void JavaBoundObject::EnsureMethodsAreSetUp() const {
937 if (are_methods_set_up_)
939 are_methods_set_up_ = true;
941 JNIEnv* env = AttachCurrentThread();
943 object_get_class_method_id_ = GetMethodIDFromClassName(
947 kReturningJavaLangClass);
949 ScopedJavaLocalRef<jobject> obj = java_object_.get(env);
955 ScopedJavaLocalRef<jclass> clazz(env, static_cast<jclass>(
956 env->CallObjectMethod(obj.obj(), object_get_class_method_id_)));
958 ScopedJavaLocalRef<jobjectArray> methods(env, static_cast<jobjectArray>(
959 env->CallObjectMethod(clazz.obj(), GetMethodIDFromClassName(
963 kReturningJavaLangReflectMethodArray))));
965 size_t num_methods = env->GetArrayLength(methods.obj());
966 // Java objects always have public methods.
969 for (size_t i = 0; i < num_methods; ++i) {
970 ScopedJavaLocalRef<jobject> java_method(
972 env->GetObjectArrayElement(methods.obj(), i));
974 if (!safe_annotation_clazz_.is_null()) {
975 jboolean safe = env->CallBooleanMethod(java_method.obj(),
976 GetMethodIDFromClassName(
978 kJavaLangReflectMethod,
979 kIsAnnotationPresent,
980 kTakesJavaLangClassReturningBoolean),
981 safe_annotation_clazz_.obj());
987 JavaMethod* method = new JavaMethod(java_method);
988 methods_.insert(std::make_pair(method->name(), method));
993 void JavaBoundObject::ThrowSecurityException(const char* message) {
994 DCHECK_CURRENTLY_ON(BrowserThread::UI);
995 JNIEnv* env = AttachCurrentThread();
996 base::android::ScopedJavaLocalRef<jclass> clazz(
997 env, env->FindClass(kJavaLangSecurityExceptionClass));
998 env->ThrowNew(clazz.obj(), message);
1001 } // namespace content