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