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