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/public/browser/browser_thread.h"
15#include "third_party/WebKit/public/web/WebBindings.h"
16
17using base::StringPrintf;
18using base::android::AttachCurrentThread;
19using base::android::ConvertUTF8ToJavaString;
20using base::android::GetClass;
21using base::android::GetMethodIDFromClassName;
22using base::android::JavaRef;
23using base::android::ScopedJavaGlobalRef;
24using base::android::ScopedJavaLocalRef;
25using 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
35namespace content {
36namespace {
37
38const char kJavaLangClass[] = "java/lang/Class";
39const char kJavaLangObject[] = "java/lang/Object";
40const char kJavaLangReflectMethod[] = "java/lang/reflect/Method";
41const char kGetClass[] = "getClass";
42const char kGetMethods[] = "getMethods";
43const char kIsAnnotationPresent[] = "isAnnotationPresent";
44const char kReturningJavaLangClass[] = "()Ljava/lang/Class;";
45const char kReturningJavaLangReflectMethodArray[] =
46    "()[Ljava/lang/reflect/Method;";
47const 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.
53struct 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
69const 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
83NPObject* JavaNPObject::Allocate(NPP npp, NPClass* np_class) {
84  JavaNPObject* obj = new JavaNPObject();
85  return obj;
86}
87
88void JavaNPObject::Deallocate(NPObject* np_object) {
89  JavaNPObject* obj = reinterpret_cast<JavaNPObject*>(np_object);
90  delete obj->bound_object;
91  delete obj;
92}
93
94bool 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
100bool 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
108bool 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
115bool 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.
128bool 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
226double 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.
234jlong 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.
254jint 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
262jvalue 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
341jvalue 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
389jvalue 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.
442jobject 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.
479void 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
531void 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
542jvalue 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.
547jobject 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
622jvalue 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
694jvalue 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.
748jvalue 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
778NPObject* 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
793JavaBoundObject::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
812JavaBoundObject::~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
821ScopedJavaLocalRef<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
827bool JavaBoundObject::HasMethod(const std::string& name) const {
828  EnsureMethodsAreSetUp();
829  return methods_.find(name) != methods_.end();
830}
831
832bool 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
885void 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
939