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/strings/string_number_conversions.h"
11 #include "base/strings/stringprintf.h"
12 #include "content/browser/renderer_host/java/java_bridge_dispatcher_host_manager.h"
13 #include "content/browser/renderer_host/java/java_type.h"
14 #include "content/browser/renderer_host/java/jni_helper.h"
15 #include "content/public/browser/browser_thread.h"
16 #include "third_party/WebKit/public/web/WebBindings.h"
18 using base::StringPrintf
;
19 using base::android::AttachCurrentThread
;
20 using base::android::ConvertUTF8ToJavaString
;
21 using base::android::GetClass
;
22 using base::android::JavaRef
;
23 using base::android::ScopedJavaGlobalRef
;
24 using base::android::ScopedJavaLocalRef
;
25 using blink::WebBindings
;
27 // The conversion between JavaScript and Java types is based on the Live
28 // Connect 2 spec. See
29 // http://jdk6.java.net/plugin2/liveconnect/#JS_JAVA_CONVERSIONS.
31 // Note that in some cases, we differ from from the spec in order to maintain
32 // existing behavior. These areas are marked LIVECONNECT_COMPLIANCE. We may
33 // revisit this decision in the future.
38 const char kJavaLangClass
[] = "java/lang/Class";
39 const char kJavaLangObject
[] = "java/lang/Object";
40 const char kJavaLangReflectMethod
[] = "java/lang/reflect/Method";
41 const char kGetClass
[] = "getClass";
42 const char kGetMethods
[] = "getMethods";
43 const char kIsAnnotationPresent
[] = "isAnnotationPresent";
44 const char kReturningJavaLangClass
[] = "()Ljava/lang/Class;";
45 const char kReturningJavaLangReflectMethodArray
[] =
46 "()[Ljava/lang/reflect/Method;";
47 const char kTakesJavaLangClassReturningBoolean
[] = "(Ljava/lang/Class;)Z";
49 // Our special NPObject type. We extend an NPObject with a pointer to a
50 // JavaBoundObject. We also add static methods for each of the NPObject
51 // callbacks, which are registered by our NPClass. These methods simply
52 // delegate to the private implementation methods of JavaBoundObject.
53 struct JavaNPObject
: public NPObject
{
54 JavaBoundObject
* bound_object
;
56 static const NPClass kNPClass
;
58 static NPObject
* Allocate(NPP npp
, NPClass
* np_class
);
59 static void Deallocate(NPObject
* np_object
);
60 static bool HasMethod(NPObject
* np_object
, NPIdentifier np_identifier
);
61 static bool Invoke(NPObject
* np_object
, NPIdentifier np_identifier
,
62 const NPVariant
*args
, uint32_t arg_count
,
64 static bool HasProperty(NPObject
* np_object
, NPIdentifier np_identifier
);
65 static bool GetProperty(NPObject
* np_object
, NPIdentifier np_identifier
,
69 const NPClass
JavaNPObject::kNPClass
= {
70 NP_CLASS_STRUCT_VERSION
,
71 JavaNPObject::Allocate
,
72 JavaNPObject::Deallocate
,
74 JavaNPObject::HasMethod
,
76 NULL
, // NPInvokeDefault
77 JavaNPObject::HasProperty
,
78 JavaNPObject::GetProperty
,
79 NULL
, // NPSetProperty,
80 NULL
, // NPRemoveProperty
83 NPObject
* JavaNPObject::Allocate(NPP npp
, NPClass
* np_class
) {
84 JavaNPObject
* obj
= new JavaNPObject();
88 void JavaNPObject::Deallocate(NPObject
* np_object
) {
89 JavaNPObject
* obj
= reinterpret_cast<JavaNPObject
*>(np_object
);
90 delete obj
->bound_object
;
94 bool JavaNPObject::HasMethod(NPObject
* np_object
, NPIdentifier np_identifier
) {
95 std::string
name(WebBindings::utf8FromIdentifier(np_identifier
));
96 JavaNPObject
* obj
= reinterpret_cast<JavaNPObject
*>(np_object
);
97 return obj
->bound_object
->HasMethod(name
);
100 bool JavaNPObject::Invoke(NPObject
* np_object
, NPIdentifier np_identifier
,
101 const NPVariant
* args
, uint32_t arg_count
,
103 std::string
name(WebBindings::utf8FromIdentifier(np_identifier
));
104 JavaNPObject
* obj
= reinterpret_cast<JavaNPObject
*>(np_object
);
105 return obj
->bound_object
->Invoke(name
, args
, arg_count
, result
);
108 bool JavaNPObject::HasProperty(NPObject
* np_object
,
109 NPIdentifier np_identifier
) {
110 // LIVECONNECT_COMPLIANCE: Existing behavior is to return false to indicate
111 // that the property is not present. Spec requires supporting this correctly.
115 bool JavaNPObject::GetProperty(NPObject
* np_object
,
116 NPIdentifier np_identifier
,
118 // LIVECONNECT_COMPLIANCE: Existing behavior is to return false to indicate
119 // that the property is undefined. Spec requires supporting this correctly.
123 // Calls a Java method through JNI. If the Java method raises an uncaught
124 // exception, it is cleared and this method returns false. Otherwise, this
125 // method returns true and the Java method's return value is provided as an
126 // NPVariant. Note that this method does not do any type coercion. The Java
127 // return value is simply converted to the corresponding NPAPI type.
130 const JavaType
& return_type
,
134 const JavaRef
<jclass
>& safe_annotation_clazz
,
135 const base::WeakPtr
<JavaBridgeDispatcherHostManager
>& manager
) {
136 JNIEnv
* env
= AttachCurrentThread();
137 switch (return_type
.type
) {
138 case JavaType::TypeBoolean
:
139 BOOLEAN_TO_NPVARIANT(env
->CallBooleanMethodA(object
, id
, parameters
),
142 case JavaType::TypeByte
:
143 INT32_TO_NPVARIANT(env
->CallByteMethodA(object
, id
, parameters
), *result
);
145 case JavaType::TypeChar
:
146 INT32_TO_NPVARIANT(env
->CallCharMethodA(object
, id
, parameters
), *result
);
148 case JavaType::TypeShort
:
149 INT32_TO_NPVARIANT(env
->CallShortMethodA(object
, id
, parameters
),
152 case JavaType::TypeInt
:
153 INT32_TO_NPVARIANT(env
->CallIntMethodA(object
, id
, parameters
), *result
);
155 case JavaType::TypeLong
:
156 DOUBLE_TO_NPVARIANT(env
->CallLongMethodA(object
, id
, parameters
),
159 case JavaType::TypeFloat
:
160 DOUBLE_TO_NPVARIANT(env
->CallFloatMethodA(object
, id
, parameters
),
163 case JavaType::TypeDouble
:
164 DOUBLE_TO_NPVARIANT(env
->CallDoubleMethodA(object
, id
, parameters
),
167 case JavaType::TypeVoid
:
168 env
->CallVoidMethodA(object
, id
, parameters
);
169 VOID_TO_NPVARIANT(*result
);
171 case JavaType::TypeArray
:
172 // LIVECONNECT_COMPLIANCE: Existing behavior is to not call methods that
173 // return arrays. Spec requires calling the method and converting the
174 // result to a JavaScript array.
175 VOID_TO_NPVARIANT(*result
);
177 case JavaType::TypeString
: {
178 jstring java_string
= static_cast<jstring
>(
179 env
->CallObjectMethodA(object
, id
, parameters
));
180 // If an exception was raised, we must clear it before calling most JNI
181 // methods. ScopedJavaLocalRef is liable to make such calls, so we test
183 if (base::android::ClearException(env
)) {
186 ScopedJavaLocalRef
<jstring
> scoped_java_string(env
, java_string
);
187 if (!scoped_java_string
.obj()) {
188 // LIVECONNECT_COMPLIANCE: Existing behavior is to return undefined.
189 // Spec requires returning a null string.
190 VOID_TO_NPVARIANT(*result
);
194 base::android::ConvertJavaStringToUTF8(scoped_java_string
);
195 size_t length
= str
.length();
196 // This pointer is freed in _NPN_ReleaseVariantValue in
197 // third_party/WebKit/Source/WebCore/bindings/v8/npruntime.cpp.
198 char* buffer
= static_cast<char*>(malloc(length
));
199 str
.copy(buffer
, length
, 0);
200 STRINGN_TO_NPVARIANT(buffer
, length
, *result
);
203 case JavaType::TypeObject
: {
204 // If an exception was raised, we must clear it before calling most JNI
205 // methods. ScopedJavaLocalRef is liable to make such calls, so we test
207 jobject java_object
= env
->CallObjectMethodA(object
, id
, parameters
);
208 if (base::android::ClearException(env
)) {
211 ScopedJavaLocalRef
<jobject
> scoped_java_object(env
, java_object
);
212 if (!scoped_java_object
.obj()) {
213 NULL_TO_NPVARIANT(*result
);
216 OBJECT_TO_NPVARIANT(JavaBoundObject::Create(scoped_java_object
,
217 safe_annotation_clazz
,
223 return !base::android::ClearException(env
);
226 double RoundDoubleTowardsZero(const double& x
) {
230 return x
> 0.0 ? floor(x
) : ceil(x
);
233 // Rounds to jlong using Java's type conversion rules.
234 jlong
RoundDoubleToLong(const double& x
) {
235 double intermediate
= RoundDoubleTowardsZero(x
);
236 // The int64 limits can not be converted exactly to double values, so we
237 // compare to custom constants. kint64max is 2^63 - 1, but the spacing
238 // between double values in the the range 2^62 to 2^63 is 2^10. The cast is
239 // required to silence a spurious gcc warning for integer overflow.
240 const int64 limit
= (GG_INT64_C(1) << 63) - static_cast<uint64
>(1 << 10);
242 const double kLargestDoubleLessThanInt64Max
= limit
;
243 const double kSmallestDoubleGreaterThanInt64Min
= -limit
;
244 if (intermediate
> kLargestDoubleLessThanInt64Max
) {
247 if (intermediate
< kSmallestDoubleGreaterThanInt64Min
) {
250 return static_cast<jlong
>(intermediate
);
253 // Rounds to jint using Java's type conversion rules.
254 jint
RoundDoubleToInt(const double& x
) {
255 double intermediate
= RoundDoubleTowardsZero(x
);
256 // The int32 limits cast exactly to double values.
257 intermediate
= std::min(intermediate
, static_cast<double>(kint32max
));
258 intermediate
= std::max(intermediate
, static_cast<double>(kint32min
));
259 return static_cast<jint
>(intermediate
);
262 jvalue
CoerceJavaScriptNumberToJavaValue(const NPVariant
& variant
,
263 const JavaType
& target_type
,
264 bool coerce_to_string
) {
265 // See http://jdk6.java.net/plugin2/liveconnect/#JS_NUMBER_VALUES.
267 // For conversion to numeric types, we need to replicate Java's type
268 // conversion rules. This requires that for integer values, we simply discard
269 // all but the lowest n buts, where n is the number of bits in the target
270 // type. For double values, the logic is more involved.
272 DCHECK(variant
.type
== NPVariantType_Int32
||
273 variant
.type
== NPVariantType_Double
);
274 bool is_double
= variant
.type
== NPVariantType_Double
;
275 switch (target_type
.type
) {
276 case JavaType::TypeByte
:
277 result
.b
= is_double
?
278 static_cast<jbyte
>(RoundDoubleToInt(NPVARIANT_TO_DOUBLE(variant
))) :
279 static_cast<jbyte
>(NPVARIANT_TO_INT32(variant
));
281 case JavaType::TypeChar
:
282 // LIVECONNECT_COMPLIANCE: Existing behavior is to convert double to 0.
283 // Spec requires converting doubles similarly to how we convert doubles to
284 // other numeric types.
285 result
.c
= is_double
? 0 :
286 static_cast<jchar
>(NPVARIANT_TO_INT32(variant
));
288 case JavaType::TypeShort
:
289 result
.s
= is_double
?
290 static_cast<jshort
>(RoundDoubleToInt(NPVARIANT_TO_DOUBLE(variant
))) :
291 static_cast<jshort
>(NPVARIANT_TO_INT32(variant
));
293 case JavaType::TypeInt
:
294 result
.i
= is_double
? RoundDoubleToInt(NPVARIANT_TO_DOUBLE(variant
)) :
295 NPVARIANT_TO_INT32(variant
);
297 case JavaType::TypeLong
:
298 result
.j
= is_double
? RoundDoubleToLong(NPVARIANT_TO_DOUBLE(variant
)) :
299 NPVARIANT_TO_INT32(variant
);
301 case JavaType::TypeFloat
:
302 result
.f
= is_double
? static_cast<jfloat
>(NPVARIANT_TO_DOUBLE(variant
)) :
303 NPVARIANT_TO_INT32(variant
);
305 case JavaType::TypeDouble
:
306 result
.d
= is_double
? NPVARIANT_TO_DOUBLE(variant
) :
307 NPVARIANT_TO_INT32(variant
);
309 case JavaType::TypeObject
:
310 // LIVECONNECT_COMPLIANCE: Existing behavior is to convert to null. Spec
311 // requires handling object equivalents of primitive types.
314 case JavaType::TypeString
:
315 result
.l
= coerce_to_string
?
316 ConvertUTF8ToJavaString(
317 AttachCurrentThread(),
319 base::StringPrintf("%.6lg", NPVARIANT_TO_DOUBLE(variant
)) :
320 base::Int64ToString(NPVARIANT_TO_INT32(variant
))).Release() :
323 case JavaType::TypeBoolean
:
324 // LIVECONNECT_COMPLIANCE: Existing behavior is to convert to false. Spec
325 // requires converting to false for 0 or NaN, true otherwise.
326 result
.z
= JNI_FALSE
;
328 case JavaType::TypeArray
:
329 // LIVECONNECT_COMPLIANCE: Existing behavior is to convert to null. Spec
330 // requires raising a JavaScript exception.
333 case JavaType::TypeVoid
:
334 // Conversion to void must never happen.
341 jvalue
CoerceJavaScriptBooleanToJavaValue(const NPVariant
& variant
,
342 const JavaType
& target_type
,
343 bool coerce_to_string
) {
344 // See http://jdk6.java.net/plugin2/liveconnect/#JS_BOOLEAN_VALUES.
345 DCHECK_EQ(NPVariantType_Bool
, variant
.type
);
346 bool boolean_value
= NPVARIANT_TO_BOOLEAN(variant
);
348 switch (target_type
.type
) {
349 case JavaType::TypeBoolean
:
350 result
.z
= boolean_value
? JNI_TRUE
: JNI_FALSE
;
352 case JavaType::TypeObject
:
353 // LIVECONNECT_COMPLIANCE: Existing behavior is to convert to NULL. Spec
354 // requires handling java.lang.Boolean and java.lang.Object.
357 case JavaType::TypeString
:
358 result
.l
= coerce_to_string
?
359 ConvertUTF8ToJavaString(AttachCurrentThread(),
360 boolean_value
? "true" : "false").Release() :
363 case JavaType::TypeByte
:
364 case JavaType::TypeChar
:
365 case JavaType::TypeShort
:
366 case JavaType::TypeInt
:
367 case JavaType::TypeLong
:
368 case JavaType::TypeFloat
:
369 case JavaType::TypeDouble
: {
370 // LIVECONNECT_COMPLIANCE: Existing behavior is to convert to 0. Spec
371 // requires converting to 0 or 1.
372 jvalue null_value
= {0};
376 case JavaType::TypeArray
:
377 // LIVECONNECT_COMPLIANCE: Existing behavior is to convert to NULL. Spec
378 // requires raising a JavaScript exception.
381 case JavaType::TypeVoid
:
382 // Conversion to void must never happen.
389 jvalue
CoerceJavaScriptStringToJavaValue(const NPVariant
& variant
,
390 const JavaType
& target_type
) {
391 // See http://jdk6.java.net/plugin2/liveconnect/#JS_STRING_VALUES.
392 DCHECK_EQ(NPVariantType_String
, variant
.type
);
394 switch (target_type
.type
) {
395 case JavaType::TypeString
:
396 result
.l
= ConvertUTF8ToJavaString(
397 AttachCurrentThread(),
398 base::StringPiece(NPVARIANT_TO_STRING(variant
).UTF8Characters
,
399 NPVARIANT_TO_STRING(variant
).UTF8Length
)).Release();
401 case JavaType::TypeObject
:
402 // LIVECONNECT_COMPLIANCE: Existing behavior is to convert to NULL. Spec
403 // requires handling java.lang.Object.
406 case JavaType::TypeByte
:
407 case JavaType::TypeShort
:
408 case JavaType::TypeInt
:
409 case JavaType::TypeLong
:
410 case JavaType::TypeFloat
:
411 case JavaType::TypeDouble
: {
412 // LIVECONNECT_COMPLIANCE: Existing behavior is to convert to 0. Spec
413 // requires using valueOf() method of corresponding object type.
414 jvalue null_value
= {0};
418 case JavaType::TypeChar
:
419 // LIVECONNECT_COMPLIANCE: Existing behavior is to convert to 0. Spec
420 // requires using java.lang.Short.decode().
423 case JavaType::TypeBoolean
:
424 // LIVECONNECT_COMPLIANCE: Existing behavior is to convert to false. Spec
425 // requires converting the empty string to false, otherwise true.
426 result
.z
= JNI_FALSE
;
428 case JavaType::TypeArray
:
429 // LIVECONNECT_COMPLIANCE: Existing behavior is to convert to NULL. Spec
430 // requires raising a JavaScript exception.
433 case JavaType::TypeVoid
:
434 // Conversion to void must never happen.
441 // Note that this only handles primitive types and strings.
442 jobject
CreateJavaArray(const JavaType
& type
, jsize length
) {
443 JNIEnv
* env
= AttachCurrentThread();
445 case JavaType::TypeBoolean
:
446 return env
->NewBooleanArray(length
);
447 case JavaType::TypeByte
:
448 return env
->NewByteArray(length
);
449 case JavaType::TypeChar
:
450 return env
->NewCharArray(length
);
451 case JavaType::TypeShort
:
452 return env
->NewShortArray(length
);
453 case JavaType::TypeInt
:
454 return env
->NewIntArray(length
);
455 case JavaType::TypeLong
:
456 return env
->NewLongArray(length
);
457 case JavaType::TypeFloat
:
458 return env
->NewFloatArray(length
);
459 case JavaType::TypeDouble
:
460 return env
->NewDoubleArray(length
);
461 case JavaType::TypeString
: {
462 ScopedJavaLocalRef
<jclass
> clazz(GetClass(env
, "java/lang/String"));
463 return env
->NewObjectArray(length
, clazz
.obj(), NULL
);
465 case JavaType::TypeVoid
:
466 // Conversion to void must never happen.
467 case JavaType::TypeArray
:
468 case JavaType::TypeObject
:
475 // Sets the specified element of the supplied array to the value of the
476 // supplied jvalue. Requires that the type of the array matches that of the
477 // jvalue. Handles only primitive types and strings. Note that in the case of a
478 // string, the array takes a new reference to the string object.
479 void SetArrayElement(jobject array
,
480 const JavaType
& type
,
482 const jvalue
& value
) {
483 JNIEnv
* env
= AttachCurrentThread();
485 case JavaType::TypeBoolean
:
486 env
->SetBooleanArrayRegion(static_cast<jbooleanArray
>(array
), index
, 1,
489 case JavaType::TypeByte
:
490 env
->SetByteArrayRegion(static_cast<jbyteArray
>(array
), index
, 1,
493 case JavaType::TypeChar
:
494 env
->SetCharArrayRegion(static_cast<jcharArray
>(array
), index
, 1,
497 case JavaType::TypeShort
:
498 env
->SetShortArrayRegion(static_cast<jshortArray
>(array
), index
, 1,
501 case JavaType::TypeInt
:
502 env
->SetIntArrayRegion(static_cast<jintArray
>(array
), index
, 1,
505 case JavaType::TypeLong
:
506 env
->SetLongArrayRegion(static_cast<jlongArray
>(array
), index
, 1,
509 case JavaType::TypeFloat
:
510 env
->SetFloatArrayRegion(static_cast<jfloatArray
>(array
), index
, 1,
513 case JavaType::TypeDouble
:
514 env
->SetDoubleArrayRegion(static_cast<jdoubleArray
>(array
), index
, 1,
517 case JavaType::TypeString
:
518 env
->SetObjectArrayElement(static_cast<jobjectArray
>(array
), index
,
521 case JavaType::TypeVoid
:
522 // Conversion to void must never happen.
523 case JavaType::TypeArray
:
524 case JavaType::TypeObject
:
528 base::android::CheckException(env
);
531 void ReleaseJavaValueIfRequired(JNIEnv
* env
,
533 const JavaType
& type
) {
534 if (type
.type
== JavaType::TypeString
||
535 type
.type
== JavaType::TypeObject
||
536 type
.type
== JavaType::TypeArray
) {
537 env
->DeleteLocalRef(value
->l
);
542 jvalue
CoerceJavaScriptValueToJavaValue(const NPVariant
& variant
,
543 const JavaType
& target_type
,
544 bool coerce_to_string
);
546 // Returns a new local reference to a Java array.
547 jobject
CoerceJavaScriptObjectToArray(const NPVariant
& variant
,
548 const JavaType
& target_type
) {
549 DCHECK_EQ(JavaType::TypeArray
, target_type
.type
);
550 NPObject
* object
= NPVARIANT_TO_OBJECT(variant
);
551 DCHECK_NE(&JavaNPObject::kNPClass
, object
->_class
);
553 const JavaType
& target_inner_type
= *target_type
.inner_type
.get();
554 // LIVECONNECT_COMPLIANCE: Existing behavior is to return null for
555 // multi-dimensional arrays. Spec requires handling multi-demensional arrays.
556 if (target_inner_type
.type
== JavaType::TypeArray
) {
560 // LIVECONNECT_COMPLIANCE: Existing behavior is to return null for object
561 // arrays. Spec requires handling object arrays.
562 if (target_inner_type
.type
== JavaType::TypeObject
) {
566 // If the object does not have a length property, return null.
567 NPVariant length_variant
;
568 if (!WebBindings::getProperty(0, object
,
569 WebBindings::getStringIdentifier("length"),
571 WebBindings::releaseVariantValue(&length_variant
);
575 // If the length property does not have numeric type, or is outside the valid
576 // range for a Java array length, return null.
578 if (NPVARIANT_IS_INT32(length_variant
)
579 && NPVARIANT_TO_INT32(length_variant
) >= 0) {
580 length
= NPVARIANT_TO_INT32(length_variant
);
581 } else if (NPVARIANT_IS_DOUBLE(length_variant
)
582 && NPVARIANT_TO_DOUBLE(length_variant
) >= 0.0
583 && NPVARIANT_TO_DOUBLE(length_variant
) <= kint32max
) {
584 length
= static_cast<jsize
>(NPVARIANT_TO_DOUBLE(length_variant
));
586 WebBindings::releaseVariantValue(&length_variant
);
591 // Create the Java array.
592 // TODO(steveblock): Handle failure to create the array.
593 jobject result
= CreateJavaArray(target_inner_type
, length
);
594 NPVariant value_variant
;
595 JNIEnv
* env
= AttachCurrentThread();
596 for (jsize i
= 0; i
< length
; ++i
) {
597 // It seems that getProperty() will set the variant to type void on failure,
598 // but this doesn't seem to be documented, so do it explicitly here for
600 VOID_TO_NPVARIANT(value_variant
);
601 // If this fails, for example due to a missing element, we simply treat the
602 // value as JavaScript undefined.
603 WebBindings::getProperty(0, object
, WebBindings::getIntIdentifier(i
),
605 jvalue element
= CoerceJavaScriptValueToJavaValue(value_variant
,
608 SetArrayElement(result
, target_inner_type
, i
, element
);
609 // CoerceJavaScriptValueToJavaValue() creates new local references to
610 // strings, objects and arrays. Of these, only strings can occur here.
611 // SetArrayElement() causes the array to take its own reference to the
612 // string, so we can now release the local reference.
613 DCHECK_NE(JavaType::TypeObject
, target_inner_type
.type
);
614 DCHECK_NE(JavaType::TypeArray
, target_inner_type
.type
);
615 ReleaseJavaValueIfRequired(env
, &element
, target_inner_type
);
616 WebBindings::releaseVariantValue(&value_variant
);
622 jvalue
CoerceJavaScriptObjectToJavaValue(const NPVariant
& variant
,
623 const JavaType
& target_type
,
624 bool coerce_to_string
) {
625 // This covers both JavaScript objects (including arrays) and Java objects.
626 // See http://jdk6.java.net/plugin2/liveconnect/#JS_OTHER_OBJECTS,
627 // http://jdk6.java.net/plugin2/liveconnect/#JS_ARRAY_VALUES and
628 // http://jdk6.java.net/plugin2/liveconnect/#JS_JAVA_OBJECTS
629 DCHECK_EQ(NPVariantType_Object
, variant
.type
);
631 NPObject
* object
= NPVARIANT_TO_OBJECT(variant
);
632 bool is_java_object
= &JavaNPObject::kNPClass
== object
->_class
;
635 switch (target_type
.type
) {
636 case JavaType::TypeObject
:
637 if (is_java_object
) {
638 // LIVECONNECT_COMPLIANCE: Existing behavior is to pass all Java
639 // objects. Spec requires passing only Java objects which are
640 // assignment-compatibile.
641 result
.l
= AttachCurrentThread()->NewLocalRef(
642 JavaBoundObject::GetJavaObject(object
).obj());
644 // LIVECONNECT_COMPLIANCE: Existing behavior is to pass null. Spec
645 // requires converting if the target type is
646 // netscape.javascript.JSObject, otherwise raising a JavaScript
651 case JavaType::TypeString
:
652 // LIVECONNECT_COMPLIANCE: Existing behavior is to convert to
653 // "undefined". Spec requires calling toString() on the Java object.
654 result
.l
= coerce_to_string
?
655 ConvertUTF8ToJavaString(AttachCurrentThread(), "undefined").
659 case JavaType::TypeByte
:
660 case JavaType::TypeShort
:
661 case JavaType::TypeInt
:
662 case JavaType::TypeLong
:
663 case JavaType::TypeFloat
:
664 case JavaType::TypeDouble
:
665 case JavaType::TypeChar
: {
666 // LIVECONNECT_COMPLIANCE: Existing behavior is to convert to 0. Spec
667 // requires raising a JavaScript exception.
668 jvalue null_value
= {0};
672 case JavaType::TypeBoolean
:
673 // LIVECONNECT_COMPLIANCE: Existing behavior is to convert to false. Spec
674 // requires raising a JavaScript exception.
675 result
.z
= JNI_FALSE
;
677 case JavaType::TypeArray
:
678 if (is_java_object
) {
679 // LIVECONNECT_COMPLIANCE: Existing behavior is to convert to NULL. Spec
680 // requires raising a JavaScript exception.
683 result
.l
= CoerceJavaScriptObjectToArray(variant
, target_type
);
686 case JavaType::TypeVoid
:
687 // Conversion to void must never happen.
694 jvalue
CoerceJavaScriptNullOrUndefinedToJavaValue(const NPVariant
& variant
,
695 const JavaType
& target_type
,
696 bool coerce_to_string
) {
697 // See http://jdk6.java.net/plugin2/liveconnect/#JS_NULL.
698 DCHECK(variant
.type
== NPVariantType_Null
||
699 variant
.type
== NPVariantType_Void
);
701 switch (target_type
.type
) {
702 case JavaType::TypeObject
:
705 case JavaType::TypeString
:
706 // LIVECONNECT_COMPLIANCE: Existing behavior is to convert undefined to
707 // "undefined". Spec requires converting undefined to NULL.
708 result
.l
= (coerce_to_string
&& variant
.type
== NPVariantType_Void
) ?
709 ConvertUTF8ToJavaString(AttachCurrentThread(), "undefined").
713 case JavaType::TypeByte
:
714 case JavaType::TypeChar
:
715 case JavaType::TypeShort
:
716 case JavaType::TypeInt
:
717 case JavaType::TypeLong
:
718 case JavaType::TypeFloat
:
719 case JavaType::TypeDouble
: {
720 jvalue null_value
= {0};
724 case JavaType::TypeBoolean
:
725 result
.z
= JNI_FALSE
;
727 case JavaType::TypeArray
:
728 // LIVECONNECT_COMPLIANCE: Existing behavior is to convert to NULL. Spec
729 // requires raising a JavaScript exception.
732 case JavaType::TypeVoid
:
733 // Conversion to void must never happen.
740 // coerce_to_string means that we should try to coerce all JavaScript values to
741 // strings when required, rather than simply converting to NULL. This is used
742 // to maintain current behaviour, which differs slightly depending upon whether
743 // or not the coercion in question is for an array element.
745 // Note that the jvalue returned by this method may contain a new local
746 // reference to an object (string, object or array). This must be released by
748 jvalue
CoerceJavaScriptValueToJavaValue(const NPVariant
& variant
,
749 const JavaType
& target_type
,
750 bool coerce_to_string
) {
751 // Note that in all these conversions, the relevant field of the jvalue must
752 // always be explicitly set, as jvalue does not initialize its fields.
754 switch (variant
.type
) {
755 case NPVariantType_Int32
:
756 case NPVariantType_Double
:
757 return CoerceJavaScriptNumberToJavaValue(variant
, target_type
,
759 case NPVariantType_Bool
:
760 return CoerceJavaScriptBooleanToJavaValue(variant
, target_type
,
762 case NPVariantType_String
:
763 return CoerceJavaScriptStringToJavaValue(variant
, target_type
);
764 case NPVariantType_Object
:
765 return CoerceJavaScriptObjectToJavaValue(variant
, target_type
,
767 case NPVariantType_Null
:
768 case NPVariantType_Void
:
769 return CoerceJavaScriptNullOrUndefinedToJavaValue(variant
, target_type
,
778 NPObject
* JavaBoundObject::Create(
779 const JavaRef
<jobject
>& object
,
780 const JavaRef
<jclass
>& safe_annotation_clazz
,
781 const base::WeakPtr
<JavaBridgeDispatcherHostManager
>& manager
) {
782 // The first argument (a plugin's instance handle) is passed through to the
783 // allocate function directly, and we don't use it, so it's ok to be 0.
784 // The object is created with a ref count of one.
785 NPObject
* np_object
= WebBindings::createObject(0, const_cast<NPClass
*>(
786 &JavaNPObject::kNPClass
));
787 // The NPObject takes ownership of the JavaBoundObject.
788 reinterpret_cast<JavaNPObject
*>(np_object
)->bound_object
=
789 new JavaBoundObject(object
, safe_annotation_clazz
, manager
);
793 JavaBoundObject::JavaBoundObject(
794 const JavaRef
<jobject
>& object
,
795 const JavaRef
<jclass
>& safe_annotation_clazz
,
796 const base::WeakPtr
<JavaBridgeDispatcherHostManager
>& manager
)
797 : java_object_(AttachCurrentThread(), object
.obj()),
799 are_methods_set_up_(false),
800 safe_annotation_clazz_(safe_annotation_clazz
) {
801 BrowserThread::PostTask(
802 BrowserThread::UI
, FROM_HERE
,
803 base::Bind(&JavaBridgeDispatcherHostManager::JavaBoundObjectCreated
,
805 base::android::ScopedJavaGlobalRef
<jobject
>(object
)));
806 // Other than informing the JavaBridgeDispatcherHostManager that a java bound
807 // object has been created (above), we don't do anything else with our Java
808 // object when first created. We do it all lazily when a method is first
812 JavaBoundObject::~JavaBoundObject() {
813 BrowserThread::PostTask(
814 BrowserThread::UI
, FROM_HERE
,
815 base::Bind(&JavaBridgeDispatcherHostManager::JavaBoundObjectDestroyed
,
817 base::android::ScopedJavaGlobalRef
<jobject
>(
818 java_object_
.get(AttachCurrentThread()))));
821 ScopedJavaLocalRef
<jobject
> JavaBoundObject::GetJavaObject(NPObject
* object
) {
822 DCHECK_EQ(&JavaNPObject::kNPClass
, object
->_class
);
823 JavaBoundObject
* jbo
= reinterpret_cast<JavaNPObject
*>(object
)->bound_object
;
824 return jbo
->java_object_
.get(AttachCurrentThread());
827 bool JavaBoundObject::HasMethod(const std::string
& name
) const {
828 EnsureMethodsAreSetUp();
829 return methods_
.find(name
) != methods_
.end();
832 bool JavaBoundObject::Invoke(const std::string
& name
, const NPVariant
* args
,
833 size_t arg_count
, NPVariant
* result
) {
834 EnsureMethodsAreSetUp();
836 // Get all methods with the correct name.
837 std::pair
<JavaMethodMap::const_iterator
, JavaMethodMap::const_iterator
>
838 iters
= methods_
.equal_range(name
);
839 if (iters
.first
== iters
.second
) {
843 // Take the first method with the correct number of arguments.
844 JavaMethod
* method
= NULL
;
845 for (JavaMethodMap::const_iterator iter
= iters
.first
; iter
!= iters
.second
;
847 if (iter
->second
->num_parameters() == arg_count
) {
848 method
= iter
->second
.get();
857 std::vector
<jvalue
> parameters(arg_count
);
858 for (size_t i
= 0; i
< arg_count
; ++i
) {
859 parameters
[i
] = CoerceJavaScriptValueToJavaValue(args
[i
],
860 method
->parameter_type(i
),
864 ScopedJavaLocalRef
<jobject
> obj
= java_object_
.get(AttachCurrentThread());
867 if (!obj
.is_null()) {
869 ok
= CallJNIMethod(obj
.obj(), method
->return_type(),
870 method
->id(), ¶meters
[0], result
,
871 safe_annotation_clazz_
,
875 // Now that we're done with the jvalue, release any local references created
876 // by CoerceJavaScriptValueToJavaValue().
877 JNIEnv
* env
= AttachCurrentThread();
878 for (size_t i
= 0; i
< arg_count
; ++i
) {
879 ReleaseJavaValueIfRequired(env
, ¶meters
[i
], method
->parameter_type(i
));
885 void JavaBoundObject::EnsureMethodsAreSetUp() const {
886 if (are_methods_set_up_
)
888 are_methods_set_up_
= true;
890 JNIEnv
* env
= AttachCurrentThread();
891 ScopedJavaLocalRef
<jobject
> obj
= java_object_
.get(env
);
897 ScopedJavaLocalRef
<jclass
> clazz(env
, static_cast<jclass
>(
898 env
->CallObjectMethod(obj
.obj(), GetMethodIDFromClassName(
902 kReturningJavaLangClass
))));
904 ScopedJavaLocalRef
<jobjectArray
> methods(env
, static_cast<jobjectArray
>(
905 env
->CallObjectMethod(clazz
.obj(), GetMethodIDFromClassName(
909 kReturningJavaLangReflectMethodArray
))));
911 size_t num_methods
= env
->GetArrayLength(methods
.obj());
912 // Java objects always have public methods.
915 for (size_t i
= 0; i
< num_methods
; ++i
) {
916 ScopedJavaLocalRef
<jobject
> java_method(
918 env
->GetObjectArrayElement(methods
.obj(), i
));
920 if (!safe_annotation_clazz_
.is_null()) {
921 jboolean safe
= env
->CallBooleanMethod(java_method
.obj(),
922 GetMethodIDFromClassName(
924 kJavaLangReflectMethod
,
925 kIsAnnotationPresent
,
926 kTakesJavaLangClassReturningBoolean
),
927 safe_annotation_clazz_
.obj());
933 JavaMethod
* method
= new JavaMethod(java_method
);
934 methods_
.insert(std::make_pair(method
->name(), method
));
938 } // namespace content