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