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