Upstream version 7.36.149.0
[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/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"
18
19 using base::StringPrintf;
20 using base::android::AttachCurrentThread;
21 using base::android::ConvertUTF8ToJavaString;
22 using base::android::GetClass;
23 using base::android::JavaRef;
24 using base::android::ScopedJavaGlobalRef;
25 using base::android::ScopedJavaLocalRef;
26 using blink::WebBindings;
27
28 // The conversion between JavaScript and Java types is based on the Live
29 // Connect 2 spec. See
30 // http://jdk6.java.net/plugin2/liveconnect/#JS_JAVA_CONVERSIONS.
31
32 // Note that in some cases, we differ from from the spec in order to maintain
33 // existing behavior. These areas are marked LIVECONNECT_COMPLIANCE. We may
34 // revisit this decision in the future.
35
36 namespace content {
37 namespace {
38
39 const char kJavaLangClass[] = "java/lang/Class";
40 const char kJavaLangObject[] = "java/lang/Object";
41 const char kJavaLangReflectMethod[] = "java/lang/reflect/Method";
42 const char kJavaLangSecurityExceptionClass[] = "java/lang/SecurityException";
43 const char kGetClass[] = "getClass";
44 const char kGetMethods[] = "getMethods";
45 const char kIsAnnotationPresent[] = "isAnnotationPresent";
46 const char kReturningJavaLangClass[] = "()Ljava/lang/Class;";
47 const char kReturningJavaLangReflectMethodArray[] =
48     "()[Ljava/lang/reflect/Method;";
49 const char kTakesJavaLangClassReturningBoolean[] = "(Ljava/lang/Class;)Z";
50 // This is an exception message, so no need to localize.
51 const char kAccessToObjectGetClassIsBlocked[] =
52     "Access to java.lang.Object.getClass is blocked";
53
54 // Our special NPObject type.  We extend an NPObject with a pointer to a
55 // JavaBoundObject.  We also add static methods for each of the NPObject
56 // callbacks, which are registered by our NPClass. These methods simply
57 // delegate to the private implementation methods of JavaBoundObject.
58 struct JavaNPObject : public NPObject {
59   JavaBoundObject* bound_object;
60
61   static const NPClass kNPClass;
62
63   static NPObject* Allocate(NPP npp, NPClass* np_class);
64   static void Deallocate(NPObject* np_object);
65   static bool HasMethod(NPObject* np_object, NPIdentifier np_identifier);
66   static bool Invoke(NPObject* np_object, NPIdentifier np_identifier,
67                      const NPVariant *args, uint32_t arg_count,
68                      NPVariant *result);
69   static bool HasProperty(NPObject* np_object, NPIdentifier np_identifier);
70   static bool GetProperty(NPObject* np_object, NPIdentifier np_identifier,
71                           NPVariant *result);
72   static bool Enumerate(NPObject* object, NPIdentifier** values,
73                         uint32_t* count);
74 };
75
76 const NPClass JavaNPObject::kNPClass = {
77   NP_CLASS_STRUCT_VERSION,
78   JavaNPObject::Allocate,
79   JavaNPObject::Deallocate,
80   NULL,  // NPInvalidate
81   JavaNPObject::HasMethod,
82   JavaNPObject::Invoke,
83   NULL,  // NPInvokeDefault
84   JavaNPObject::HasProperty,
85   JavaNPObject::GetProperty,
86   NULL,  // NPSetProperty,
87   NULL,  // NPRemoveProperty
88   JavaNPObject::Enumerate,
89   NULL,
90 };
91
92 NPObject* JavaNPObject::Allocate(NPP npp, NPClass* np_class) {
93   JavaNPObject* obj = new JavaNPObject();
94   return obj;
95 }
96
97 void JavaNPObject::Deallocate(NPObject* np_object) {
98   JavaNPObject* obj = reinterpret_cast<JavaNPObject*>(np_object);
99   delete obj->bound_object;
100   delete obj;
101 }
102
103 bool JavaNPObject::HasMethod(NPObject* np_object, NPIdentifier np_identifier) {
104   std::string name(WebBindings::utf8FromIdentifier(np_identifier));
105   JavaNPObject* obj = reinterpret_cast<JavaNPObject*>(np_object);
106   return obj->bound_object->HasMethod(name);
107 }
108
109 bool JavaNPObject::Invoke(NPObject* np_object, NPIdentifier np_identifier,
110                           const NPVariant* args, uint32_t arg_count,
111                           NPVariant* result) {
112   std::string name(WebBindings::utf8FromIdentifier(np_identifier));
113   JavaNPObject* obj = reinterpret_cast<JavaNPObject*>(np_object);
114   return obj->bound_object->Invoke(name, args, arg_count, result);
115 }
116
117 bool JavaNPObject::HasProperty(NPObject* np_object,
118                                NPIdentifier np_identifier) {
119   // LIVECONNECT_COMPLIANCE: Existing behavior is to return false to indicate
120   // that the property is not present. Spec requires supporting this correctly.
121   return false;
122 }
123
124 bool JavaNPObject::GetProperty(NPObject* np_object,
125                                NPIdentifier np_identifier,
126                                NPVariant* result) {
127   // LIVECONNECT_COMPLIANCE: Existing behavior is to return false to indicate
128   // that the property is undefined. Spec requires supporting this correctly.
129   return false;
130 }
131
132 bool JavaNPObject::Enumerate(NPObject* np_object, NPIdentifier** values,
133                              uint32_t* count) {
134   JavaNPObject* obj = reinterpret_cast<JavaNPObject*>(np_object);
135   if (!obj->bound_object->CanEnumerateMethods()) return false;
136   std::vector<std::string> method_names = obj->bound_object->GetMethodNames();
137   *count = base::saturated_cast<uint32_t>(method_names.size());
138   *values = static_cast<NPIdentifier*>(calloc(*count, sizeof(NPIdentifier)));
139   for (uint32_t i = 0; i < *count; ++i) {
140     (*values)[i] = WebBindings::getStringIdentifier(method_names[i].c_str());
141   }
142   return true;
143 }
144
145 // Calls a Java method through JNI. If the Java method raises an uncaught
146 // exception, it is cleared and this method returns false. Otherwise, this
147 // method returns true and the Java method's return value is provided as an
148 // NPVariant. Note that this method does not do any type coercion. The Java
149 // return value is simply converted to the corresponding NPAPI type.
150 bool CallJNIMethod(
151     jobject object,
152     const JavaType& return_type,
153     jmethodID id,
154     jvalue* parameters,
155     NPVariant* result,
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),
163                            *result);
164       break;
165     case JavaType::TypeByte:
166       INT32_TO_NPVARIANT(env->CallByteMethodA(object, id, parameters), *result);
167       break;
168     case JavaType::TypeChar:
169       INT32_TO_NPVARIANT(env->CallCharMethodA(object, id, parameters), *result);
170       break;
171     case JavaType::TypeShort:
172       INT32_TO_NPVARIANT(env->CallShortMethodA(object, id, parameters),
173                          *result);
174       break;
175     case JavaType::TypeInt:
176       INT32_TO_NPVARIANT(env->CallIntMethodA(object, id, parameters), *result);
177       break;
178     case JavaType::TypeLong:
179       DOUBLE_TO_NPVARIANT(env->CallLongMethodA(object, id, parameters),
180                           *result);
181       break;
182     case JavaType::TypeFloat:
183       DOUBLE_TO_NPVARIANT(env->CallFloatMethodA(object, id, parameters),
184                           *result);
185       break;
186     case JavaType::TypeDouble:
187       DOUBLE_TO_NPVARIANT(env->CallDoubleMethodA(object, id, parameters),
188                           *result);
189       break;
190     case JavaType::TypeVoid:
191       env->CallVoidMethodA(object, id, parameters);
192       VOID_TO_NPVARIANT(*result);
193       break;
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);
199       break;
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
205       // first.
206       if (base::android::ClearException(env)) {
207         return false;
208       }
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);
214         break;
215       }
216       std::string str =
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);
224       break;
225     }
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
229       // first.
230       jobject java_object = env->CallObjectMethodA(object, id, parameters);
231       if (base::android::ClearException(env)) {
232         return false;
233       }
234       ScopedJavaLocalRef<jobject> scoped_java_object(env, java_object);
235       if (!scoped_java_object.obj()) {
236         NULL_TO_NPVARIANT(*result);
237         break;
238       }
239       OBJECT_TO_NPVARIANT(JavaBoundObject::Create(scoped_java_object,
240                                                   safe_annotation_clazz,
241                                                   manager,
242                                                   can_enumerate_methods),
243                           *result);
244       break;
245     }
246   }
247   return !base::android::ClearException(env);
248 }
249
250 double RoundDoubleTowardsZero(const double& x) {
251   if (std::isnan(x)) {
252     return 0.0;
253   }
254   return x > 0.0 ? floor(x) : ceil(x);
255 }
256
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);
265   DCHECK(limit > 0);
266   const double kLargestDoubleLessThanInt64Max = limit;
267   const double kSmallestDoubleGreaterThanInt64Min = -limit;
268   if (intermediate > kLargestDoubleLessThanInt64Max) {
269     return kint64max;
270   }
271   if (intermediate < kSmallestDoubleGreaterThanInt64Min) {
272     return kint64min;
273   }
274   return static_cast<jlong>(intermediate);
275 }
276
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);
284 }
285
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.
290
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.
295   jvalue result;
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));
304       break;
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));
311       break;
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));
316       break;
317     case JavaType::TypeInt:
318       result.i = is_double ? RoundDoubleToInt(NPVARIANT_TO_DOUBLE(variant)) :
319                              NPVARIANT_TO_INT32(variant);
320       break;
321     case JavaType::TypeLong:
322       result.j = is_double ? RoundDoubleToLong(NPVARIANT_TO_DOUBLE(variant)) :
323                              NPVARIANT_TO_INT32(variant);
324       break;
325     case JavaType::TypeFloat:
326       result.f = is_double ? static_cast<jfloat>(NPVARIANT_TO_DOUBLE(variant)) :
327                              NPVARIANT_TO_INT32(variant);
328       break;
329     case JavaType::TypeDouble:
330       result.d = is_double ? NPVARIANT_TO_DOUBLE(variant) :
331                              NPVARIANT_TO_INT32(variant);
332       break;
333     case JavaType::TypeObject:
334       // LIVECONNECT_COMPLIANCE: Existing behavior is to convert to null. Spec
335       // requires handling object equivalents of primitive types.
336       result.l = NULL;
337       break;
338     case JavaType::TypeString:
339       result.l = coerce_to_string ?
340           ConvertUTF8ToJavaString(
341               AttachCurrentThread(),
342               is_double ?
343                   base::StringPrintf("%.6lg", NPVARIANT_TO_DOUBLE(variant)) :
344                   base::Int64ToString(NPVARIANT_TO_INT32(variant))).Release() :
345           NULL;
346       break;
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;
351       break;
352     case JavaType::TypeArray:
353       // LIVECONNECT_COMPLIANCE: Existing behavior is to convert to null. Spec
354       // requires raising a JavaScript exception.
355       result.l = NULL;
356       break;
357     case JavaType::TypeVoid:
358       // Conversion to void must never happen.
359       NOTREACHED();
360       break;
361   }
362   return result;
363 }
364
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);
371   jvalue result;
372   switch (target_type.type) {
373     case JavaType::TypeBoolean:
374       result.z = boolean_value ? JNI_TRUE : JNI_FALSE;
375       break;
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.
379       result.l = NULL;
380       break;
381     case JavaType::TypeString:
382       result.l = coerce_to_string ?
383           ConvertUTF8ToJavaString(AttachCurrentThread(),
384                                   boolean_value ? "true" : "false").Release() :
385           NULL;
386       break;
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};
397       result = null_value;
398       break;
399     }
400     case JavaType::TypeArray:
401       // LIVECONNECT_COMPLIANCE: Existing behavior is to convert to NULL. Spec
402       // requires raising a JavaScript exception.
403       result.l = NULL;
404       break;
405     case JavaType::TypeVoid:
406       // Conversion to void must never happen.
407       NOTREACHED();
408       break;
409   }
410   return result;
411 }
412
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);
417   jvalue result;
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();
424       break;
425     case JavaType::TypeObject:
426       // LIVECONNECT_COMPLIANCE: Existing behavior is to convert to NULL. Spec
427       // requires handling java.lang.Object.
428       result.l = NULL;
429       break;
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};
439       result = null_value;
440       break;
441     }
442     case JavaType::TypeChar:
443       // LIVECONNECT_COMPLIANCE: Existing behavior is to convert to 0. Spec
444       // requires using java.lang.Short.decode().
445       result.c = 0;
446       break;
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;
451       break;
452     case JavaType::TypeArray:
453       // LIVECONNECT_COMPLIANCE: Existing behavior is to convert to NULL. Spec
454       // requires raising a JavaScript exception.
455       result.l = NULL;
456       break;
457     case JavaType::TypeVoid:
458       // Conversion to void must never happen.
459       NOTREACHED();
460       break;
461   }
462   return result;
463 }
464
465 // Note that this only handles primitive types and strings.
466 jobject CreateJavaArray(const JavaType& type, jsize length) {
467   JNIEnv* env = AttachCurrentThread();
468   switch (type.type) {
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);
488     }
489     case JavaType::TypeVoid:
490       // Conversion to void must never happen.
491     case JavaType::TypeArray:
492     case JavaType::TypeObject:
493       // Not handled.
494       NOTREACHED();
495   }
496   return NULL;
497 }
498
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,
505                      jsize index,
506                      const jvalue& value) {
507   JNIEnv* env = AttachCurrentThread();
508   switch (type.type) {
509     case JavaType::TypeBoolean:
510       env->SetBooleanArrayRegion(static_cast<jbooleanArray>(array), index, 1,
511                                  &value.z);
512       break;
513     case JavaType::TypeByte:
514       env->SetByteArrayRegion(static_cast<jbyteArray>(array), index, 1,
515                               &value.b);
516       break;
517     case JavaType::TypeChar:
518       env->SetCharArrayRegion(static_cast<jcharArray>(array), index, 1,
519                               &value.c);
520       break;
521     case JavaType::TypeShort:
522       env->SetShortArrayRegion(static_cast<jshortArray>(array), index, 1,
523                                &value.s);
524       break;
525     case JavaType::TypeInt:
526       env->SetIntArrayRegion(static_cast<jintArray>(array), index, 1,
527                              &value.i);
528       break;
529     case JavaType::TypeLong:
530       env->SetLongArrayRegion(static_cast<jlongArray>(array), index, 1,
531                               &value.j);
532       break;
533     case JavaType::TypeFloat:
534       env->SetFloatArrayRegion(static_cast<jfloatArray>(array), index, 1,
535                                &value.f);
536       break;
537     case JavaType::TypeDouble:
538       env->SetDoubleArrayRegion(static_cast<jdoubleArray>(array), index, 1,
539                                 &value.d);
540       break;
541     case JavaType::TypeString:
542       env->SetObjectArrayElement(static_cast<jobjectArray>(array), index,
543                                  value.l);
544       break;
545     case JavaType::TypeVoid:
546       // Conversion to void must never happen.
547     case JavaType::TypeArray:
548     case JavaType::TypeObject:
549       // Not handled.
550       NOTREACHED();
551   }
552   base::android::CheckException(env);
553 }
554
555 void ReleaseJavaValueIfRequired(JNIEnv* env,
556                                 jvalue* value,
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);
562     value->l = NULL;
563   }
564 }
565
566 jvalue CoerceJavaScriptValueToJavaValue(const NPVariant& variant,
567                                         const JavaType& target_type,
568                                         bool coerce_to_string);
569
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);
576
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) {
581     return NULL;
582   }
583
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) {
587     return NULL;
588   }
589
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"),
594                                 &length_variant)) {
595     WebBindings::releaseVariantValue(&length_variant);
596     return NULL;
597   }
598
599   // If the length property does not have numeric type, or is outside the valid
600   // range for a Java array length, return null.
601   jsize length = -1;
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));
609   }
610   WebBindings::releaseVariantValue(&length_variant);
611   if (length == -1) {
612     return NULL;
613   }
614
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
623     // safety.
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),
628                              &value_variant);
629     jvalue element = CoerceJavaScriptValueToJavaValue(value_variant,
630                                                       target_inner_type,
631                                                       false);
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);
641   }
642
643   return result;
644 }
645
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);
654
655   NPObject* object = NPVARIANT_TO_OBJECT(variant);
656   bool is_java_object = &JavaNPObject::kNPClass == object->_class;
657
658   jvalue result;
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());
667       } else {
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
671         // exception.
672         result.l = NULL;
673       }
674       break;
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").
680               Release() :
681           NULL;
682       break;
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};
693       result = null_value;
694       break;
695     }
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;
700       break;
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.
705         result.l = NULL;
706       } else {
707         result.l = CoerceJavaScriptObjectToArray(variant, target_type);
708       }
709       break;
710     case JavaType::TypeVoid:
711       // Conversion to void must never happen.
712       NOTREACHED();
713       break;
714   }
715   return result;
716 }
717
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);
724   jvalue result;
725   switch (target_type.type) {
726     case JavaType::TypeObject:
727       result.l = NULL;
728       break;
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").
734               Release() :
735           NULL;
736       break;
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};
745       result = null_value;
746       break;
747     }
748     case JavaType::TypeBoolean:
749       result.z = JNI_FALSE;
750       break;
751     case JavaType::TypeArray:
752       // LIVECONNECT_COMPLIANCE: Existing behavior is to convert to NULL. Spec
753       // requires raising a JavaScript exception.
754       result.l = NULL;
755       break;
756     case JavaType::TypeVoid:
757       // Conversion to void must never happen.
758       NOTREACHED();
759       break;
760   }
761   return result;
762 }
763
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.
768 //
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
771 // the caller.
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.
777
778   switch (variant.type) {
779     case NPVariantType_Int32:
780     case NPVariantType_Double:
781       return CoerceJavaScriptNumberToJavaValue(variant, target_type,
782                                                coerce_to_string);
783     case NPVariantType_Bool:
784       return CoerceJavaScriptBooleanToJavaValue(variant, target_type,
785                                                 coerce_to_string);
786     case NPVariantType_String:
787       return CoerceJavaScriptStringToJavaValue(variant, target_type);
788     case NPVariantType_Object:
789       return CoerceJavaScriptObjectToJavaValue(variant, target_type,
790                                                coerce_to_string);
791     case NPVariantType_Null:
792     case NPVariantType_Void:
793       return CoerceJavaScriptNullOrUndefinedToJavaValue(variant, target_type,
794                                                         coerce_to_string);
795   }
796   NOTREACHED();
797   return jvalue();
798 }
799
800 }  // namespace
801
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 =
814       new JavaBoundObject(
815           object, safe_annotation_clazz, manager, can_enumerate_methods);
816   return np_object;
817 }
818
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()),
825       manager_(manager),
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,
833                    manager_,
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
838   // invoked.
839 }
840
841 JavaBoundObject::~JavaBoundObject() {
842   BrowserThread::PostTask(
843       BrowserThread::UI, FROM_HERE,
844       base::Bind(&JavaBridgeDispatcherHostManager::JavaBoundObjectDestroyed,
845                  manager_,
846                  base::android::ScopedJavaGlobalRef<jobject>(
847                      java_object_.get(AttachCurrentThread()))));
848 }
849
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());
854 }
855
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);
863   }
864   return result;
865 }
866
867 bool JavaBoundObject::HasMethod(const std::string& name) const {
868   EnsureMethodsAreSetUp();
869   return methods_.find(name) != methods_.end();
870 }
871
872 bool JavaBoundObject::Invoke(const std::string& name, const NPVariant* args,
873                              size_t arg_count, NPVariant* result) {
874   EnsureMethodsAreSetUp();
875
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) {
880     return false;
881   }
882
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;
886        ++iter) {
887     if (iter->second->num_parameters() == arg_count) {
888       method = iter->second.get();
889       break;
890     }
891   }
892   if (!method) {
893     return false;
894   }
895
896   // Coerce
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),
901                                                      true);
902   }
903
904   JNIEnv* env = AttachCurrentThread();
905   ScopedJavaLocalRef<jobject> obj = java_object_.get(env);
906
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));
914     return false;
915   }
916
917   bool ok = false;
918   if (!obj.is_null()) {
919     // Call
920     ok = CallJNIMethod(obj.obj(), method->return_type(),
921                        method->id(), &parameters[0], result,
922                        safe_annotation_clazz_,
923                        manager_,
924                        can_enumerate_methods_);
925   }
926
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, &parameters[i], method->parameter_type(i));
931   }
932
933   return ok;
934 }
935
936 void JavaBoundObject::EnsureMethodsAreSetUp() const {
937   if (are_methods_set_up_)
938     return;
939   are_methods_set_up_ = true;
940
941   JNIEnv* env = AttachCurrentThread();
942
943   object_get_class_method_id_ = GetMethodIDFromClassName(
944       env,
945       kJavaLangObject,
946       kGetClass,
947       kReturningJavaLangClass);
948
949   ScopedJavaLocalRef<jobject> obj = java_object_.get(env);
950
951   if (obj.is_null()) {
952     return;
953   }
954
955   ScopedJavaLocalRef<jclass> clazz(env, static_cast<jclass>(
956       env->CallObjectMethod(obj.obj(), object_get_class_method_id_)));
957
958   ScopedJavaLocalRef<jobjectArray> methods(env, static_cast<jobjectArray>(
959       env->CallObjectMethod(clazz.obj(), GetMethodIDFromClassName(
960           env,
961           kJavaLangClass,
962           kGetMethods,
963           kReturningJavaLangReflectMethodArray))));
964
965   size_t num_methods = env->GetArrayLength(methods.obj());
966   // Java objects always have public methods.
967   DCHECK(num_methods);
968
969   for (size_t i = 0; i < num_methods; ++i) {
970     ScopedJavaLocalRef<jobject> java_method(
971         env,
972         env->GetObjectArrayElement(methods.obj(), i));
973
974     if (!safe_annotation_clazz_.is_null()) {
975       jboolean safe = env->CallBooleanMethod(java_method.obj(),
976           GetMethodIDFromClassName(
977               env,
978               kJavaLangReflectMethod,
979               kIsAnnotationPresent,
980               kTakesJavaLangClassReturningBoolean),
981           safe_annotation_clazz_.obj());
982
983       if (!safe)
984         continue;
985     }
986
987     JavaMethod* method = new JavaMethod(java_method);
988     methods_.insert(std::make_pair(method->name(), method));
989   }
990 }
991
992 // static
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);
999 }
1000
1001 }  // namespace content