Home | History | Annotate | Download | only in java
      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 
     21 using base::StringPrintf;
     22 using base::android::AttachCurrentThread;
     23 using base::android::ConvertUTF8ToJavaString;
     24 using base::android::GetClass;
     25 using base::android::JavaRef;
     26 using base::android::ScopedJavaGlobalRef;
     27 using base::android::ScopedJavaLocalRef;
     28 using 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 
     38 namespace content {
     39 namespace {
     40 
     41 const char kJavaLangClass[] = "java/lang/Class";
     42 const char kJavaLangObject[] = "java/lang/Object";
     43 const char kJavaLangReflectMethod[] = "java/lang/reflect/Method";
     44 const char kGetClass[] = "getClass";
     45 const char kGetMethods[] = "getMethods";
     46 const char kIsAnnotationPresent[] = "isAnnotationPresent";
     47 const char kReturningJavaLangClass[] = "()Ljava/lang/Class;";
     48 const char kReturningJavaLangReflectMethodArray[] =
     49     "()[Ljava/lang/reflect/Method;";
     50 const char kTakesJavaLangClassReturningBoolean[] = "(Ljava/lang/Class;)Z";
     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 };
     73 
     74 const 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 
     88 NPObject* JavaNPObject::Allocate(NPP npp, NPClass* np_class) {
     89   JavaNPObject* obj = new JavaNPObject();
     90   return obj;
     91 }
     92 
     93 void JavaNPObject::Deallocate(NPObject* np_object) {
     94   JavaNPObject* obj = reinterpret_cast<JavaNPObject*>(np_object);
     95   delete obj->bound_object;
     96   delete obj;
     97 }
     98 
     99 bool 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 
    105 bool 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 
    113 bool 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 
    120 bool 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.
    133 bool 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 
    231 double 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.
    239 jlong 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.
    259 jint 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 
    267 jvalue 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 
    346 jvalue 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 
    394 jvalue 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.
    447 jobject 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.
    484 void 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 
    536 void 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 
    547 jvalue 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.
    552 jobject 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 
    627 jvalue 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 
    699 jvalue 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.
    753 jvalue 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 
    783 NPObject* 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 
    798 JavaBoundObject::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 
    818 JavaBoundObject::~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 
    827 ScopedJavaLocalRef<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 
    833 bool JavaBoundObject::HasMethod(const std::string& name) const {
    834   EnsureMethodsAreSetUp();
    835   return methods_.find(name) != methods_.end();
    836 }
    837 
    838 bool 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 
    907 void 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