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/gin_java_script_to_java_types_coercion.h"
      6 
      7 #include <unistd.h>
      8 
      9 #include "base/android/jni_android.h"
     10 #include "base/android/jni_string.h"
     11 #include "base/strings/string_number_conversions.h"
     12 #include "base/strings/stringprintf.h"
     13 #include "base/strings/utf_string_conversions.h"
     14 #include "content/common/android/gin_java_bridge_value.h"
     15 #include "third_party/WebKit/public/platform/WebString.h"
     16 
     17 using base::android::ScopedJavaLocalRef;
     18 
     19 namespace content {
     20 
     21 namespace {
     22 
     23 const char kJavaLangString[] = "java/lang/String";
     24 const char kUndefined[] = "undefined";
     25 
     26 // This is an intermediate solution until we fix http://crbug.com/391492.
     27 jstring ConvertUTF8ToJString(JNIEnv* env, const std::string& string) {
     28   base::string16 utf16(
     29       blink::WebString::fromUTF8(string.c_str(), string.size()));
     30   return env->NewString(utf16.data(), utf16.length());
     31 }
     32 
     33 double RoundDoubleTowardsZero(const double& x) {
     34   if (std::isnan(x)) {
     35     return 0.0;
     36   }
     37   return x > 0.0 ? floor(x) : ceil(x);
     38 }
     39 
     40 // Rounds to jlong using Java's type conversion rules.
     41 jlong RoundDoubleToLong(const double& x) {
     42   double intermediate = RoundDoubleTowardsZero(x);
     43   // The int64 limits can not be converted exactly to double values, so we
     44   // compare to custom constants. kint64max is 2^63 - 1, but the spacing
     45   // between double values in the the range 2^62 to 2^63 is 2^10. The cast is
     46   // required to silence a spurious gcc warning for integer overflow.
     47   const int64 kLimit = (GG_INT64_C(1) << 63) - static_cast<uint64>(1 << 10);
     48   DCHECK(kLimit > 0);
     49   const double kLargestDoubleLessThanInt64Max = kLimit;
     50   const double kSmallestDoubleGreaterThanInt64Min = -kLimit;
     51   if (intermediate > kLargestDoubleLessThanInt64Max) {
     52     return kint64max;
     53   }
     54   if (intermediate < kSmallestDoubleGreaterThanInt64Min) {
     55     return kint64min;
     56   }
     57   return static_cast<jlong>(intermediate);
     58 }
     59 
     60 // Rounds to jint using Java's type conversion rules.
     61 jint RoundDoubleToInt(const double& x) {
     62   double intermediate = RoundDoubleTowardsZero(x);
     63   // The int32 limits cast exactly to double values.
     64   intermediate = std::min(intermediate, static_cast<double>(kint32max));
     65   intermediate = std::max(intermediate, static_cast<double>(kint32min));
     66   return static_cast<jint>(intermediate);
     67 }
     68 
     69 jvalue CoerceJavaScriptIntegerToJavaValue(JNIEnv* env,
     70                                           const base::Value* value,
     71                                           const JavaType& target_type,
     72                                           bool coerce_to_string,
     73                                           GinJavaBridgeError* error) {
     74   // See http://jdk6.java.net/plugin2/liveconnect/#JS_NUMBER_VALUES.
     75 
     76   // For conversion to numeric types, we need to replicate Java's type
     77   // conversion rules. This requires that for integer values, we simply discard
     78   // all but the lowest n buts, where n is the number of bits in the target
     79   // type.
     80   jvalue result;
     81   int int_value;
     82   value->GetAsInteger(&int_value);
     83   switch (target_type.type) {
     84     case JavaType::TypeByte:
     85       result.b = static_cast<jbyte>(int_value);
     86       break;
     87     case JavaType::TypeChar:
     88       result.c = static_cast<jchar>(int_value);
     89       break;
     90     case JavaType::TypeShort:
     91       result.s = static_cast<jshort>(int_value);
     92       break;
     93     case JavaType::TypeInt:
     94       result.i = int_value;
     95       break;
     96     case JavaType::TypeLong:
     97       result.j = int_value;
     98       break;
     99     case JavaType::TypeFloat:
    100       result.f = int_value;
    101       break;
    102     case JavaType::TypeDouble:
    103       result.d = int_value;
    104       break;
    105     case JavaType::TypeObject:
    106       // LIVECONNECT_COMPLIANCE: Existing behavior is to convert to null. Spec
    107       // requires handling object equivalents of primitive types.
    108       result.l = NULL;
    109       break;
    110     case JavaType::TypeString:
    111       result.l = coerce_to_string
    112                      ? ConvertUTF8ToJString(env, base::Int64ToString(int_value))
    113                      : NULL;
    114       break;
    115     case JavaType::TypeBoolean:
    116       // LIVECONNECT_COMPLIANCE: Existing behavior is to convert to false. Spec
    117       // requires converting to false for 0 or NaN, true otherwise.
    118       result.z = JNI_FALSE;
    119       break;
    120     case JavaType::TypeArray:
    121       // LIVECONNECT_COMPLIANCE: Existing behavior is to convert to null. Spec
    122       // requires raising a JavaScript exception.
    123       result.l = NULL;
    124       break;
    125     case JavaType::TypeVoid:
    126       // Conversion to void must never happen.
    127       NOTREACHED();
    128       break;
    129   }
    130   return result;
    131 }
    132 
    133 jvalue CoerceJavaScriptDoubleToJavaValue(JNIEnv* env,
    134                                          double double_value,
    135                                          const JavaType& target_type,
    136                                          bool coerce_to_string,
    137                                          GinJavaBridgeError* error) {
    138   // See http://jdk6.java.net/plugin2/liveconnect/#JS_NUMBER_VALUES.
    139   // For conversion to numeric types, we need to replicate Java's type
    140   // conversion rules.
    141   jvalue result;
    142   switch (target_type.type) {
    143     case JavaType::TypeByte:
    144       result.b = static_cast<jbyte>(RoundDoubleToInt(double_value));
    145       break;
    146     case JavaType::TypeChar:
    147       // LIVECONNECT_COMPLIANCE: Existing behavior is to convert double to 0.
    148       // Spec requires converting doubles similarly to how we convert doubles to
    149       // other numeric types.
    150       result.c = 0;
    151       break;
    152     case JavaType::TypeShort:
    153       result.s = static_cast<jshort>(RoundDoubleToInt(double_value));
    154       break;
    155     case JavaType::TypeInt:
    156       result.i = RoundDoubleToInt(double_value);
    157       break;
    158     case JavaType::TypeLong:
    159       result.j = RoundDoubleToLong(double_value);
    160       break;
    161     case JavaType::TypeFloat:
    162       result.f = static_cast<jfloat>(double_value);
    163       break;
    164     case JavaType::TypeDouble:
    165       result.d = double_value;
    166       break;
    167     case JavaType::TypeObject:
    168       // LIVECONNECT_COMPLIANCE: Existing behavior is to convert to null. Spec
    169       // requires handling object equivalents of primitive types.
    170       result.l = NULL;
    171       break;
    172     case JavaType::TypeString:
    173       result.l = coerce_to_string
    174                      ? ConvertUTF8ToJString(
    175                            env, base::StringPrintf("%.6lg", double_value))
    176                      : NULL;
    177       break;
    178     case JavaType::TypeBoolean:
    179       // LIVECONNECT_COMPLIANCE: Existing behavior is to convert to false. Spec
    180       // requires converting to false for 0 or NaN, true otherwise.
    181       result.z = JNI_FALSE;
    182       break;
    183     case JavaType::TypeArray:
    184       // LIVECONNECT_COMPLIANCE: Existing behavior is to convert to null. Spec
    185       // requires raising a JavaScript exception.
    186       result.l = NULL;
    187       break;
    188     case JavaType::TypeVoid:
    189       // Conversion to void must never happen.
    190       NOTREACHED();
    191       break;
    192   }
    193   return result;
    194 }
    195 
    196 jvalue CoerceJavaScriptBooleanToJavaValue(JNIEnv* env,
    197                                           const base::Value* value,
    198                                           const JavaType& target_type,
    199                                           bool coerce_to_string,
    200                                           GinJavaBridgeError* error) {
    201   // See http://jdk6.java.net/plugin2/liveconnect/#JS_BOOLEAN_VALUES.
    202   bool boolean_value;
    203   value->GetAsBoolean(&boolean_value);
    204   jvalue result;
    205   switch (target_type.type) {
    206     case JavaType::TypeBoolean:
    207       result.z = boolean_value ? JNI_TRUE : JNI_FALSE;
    208       break;
    209     case JavaType::TypeObject:
    210       // LIVECONNECT_COMPLIANCE: Existing behavior is to convert to NULL. Spec
    211       // requires handling java.lang.Boolean and java.lang.Object.
    212       result.l = NULL;
    213       break;
    214     case JavaType::TypeString:
    215       result.l = coerce_to_string ? ConvertUTF8ToJString(
    216                                         env, boolean_value ? "true" : "false")
    217                                   : NULL;
    218       break;
    219     case JavaType::TypeByte:
    220     case JavaType::TypeChar:
    221     case JavaType::TypeShort:
    222     case JavaType::TypeInt:
    223     case JavaType::TypeLong:
    224     case JavaType::TypeFloat:
    225     case JavaType::TypeDouble: {
    226       // LIVECONNECT_COMPLIANCE: Existing behavior is to convert to 0. Spec
    227       // requires converting to 0 or 1.
    228       jvalue null_value = {0};
    229       result = null_value;
    230       break;
    231     }
    232     case JavaType::TypeArray:
    233       // LIVECONNECT_COMPLIANCE: Existing behavior is to convert to NULL. Spec
    234       // requires raising a JavaScript exception.
    235       result.l = NULL;
    236       break;
    237     case JavaType::TypeVoid:
    238       // Conversion to void must never happen.
    239       NOTREACHED();
    240       break;
    241   }
    242   return result;
    243 }
    244 
    245 jvalue CoerceJavaScriptStringToJavaValue(JNIEnv* env,
    246                                          const base::Value* value,
    247                                          const JavaType& target_type,
    248                                          GinJavaBridgeError* error) {
    249   // See http://jdk6.java.net/plugin2/liveconnect/#JS_STRING_VALUES.
    250   jvalue result;
    251   switch (target_type.type) {
    252     case JavaType::TypeString: {
    253       std::string string_result;
    254       value->GetAsString(&string_result);
    255       result.l = ConvertUTF8ToJString(env, string_result);
    256       break;
    257     }
    258     case JavaType::TypeObject:
    259       // LIVECONNECT_COMPLIANCE: Existing behavior is to convert to NULL. Spec
    260       // requires handling java.lang.Object.
    261       result.l = NULL;
    262       break;
    263     case JavaType::TypeByte:
    264     case JavaType::TypeShort:
    265     case JavaType::TypeInt:
    266     case JavaType::TypeLong:
    267     case JavaType::TypeFloat:
    268     case JavaType::TypeDouble: {
    269       // LIVECONNECT_COMPLIANCE: Existing behavior is to convert to 0. Spec
    270       // requires using valueOf() method of corresponding object type.
    271       jvalue null_value = {0};
    272       result = null_value;
    273       break;
    274     }
    275     case JavaType::TypeChar:
    276       // LIVECONNECT_COMPLIANCE: Existing behavior is to convert to 0. Spec
    277       // requires using java.lang.Short.decode().
    278       result.c = 0;
    279       break;
    280     case JavaType::TypeBoolean:
    281       // LIVECONNECT_COMPLIANCE: Existing behavior is to convert to false. Spec
    282       // requires converting the empty string to false, otherwise true.
    283       result.z = JNI_FALSE;
    284       break;
    285     case JavaType::TypeArray:
    286       // LIVECONNECT_COMPLIANCE: Existing behavior is to convert to NULL. Spec
    287       // requires raising a JavaScript exception.
    288       result.l = NULL;
    289       break;
    290     case JavaType::TypeVoid:
    291       // Conversion to void must never happen.
    292       NOTREACHED();
    293       break;
    294   }
    295   return result;
    296 }
    297 
    298 // Note that this only handles primitive types and strings.
    299 jobject CreateJavaArray(JNIEnv* env, const JavaType& type, jsize length) {
    300   switch (type.type) {
    301     case JavaType::TypeBoolean:
    302       return env->NewBooleanArray(length);
    303     case JavaType::TypeByte:
    304       return env->NewByteArray(length);
    305     case JavaType::TypeChar:
    306       return env->NewCharArray(length);
    307     case JavaType::TypeShort:
    308       return env->NewShortArray(length);
    309     case JavaType::TypeInt:
    310       return env->NewIntArray(length);
    311     case JavaType::TypeLong:
    312       return env->NewLongArray(length);
    313     case JavaType::TypeFloat:
    314       return env->NewFloatArray(length);
    315     case JavaType::TypeDouble:
    316       return env->NewDoubleArray(length);
    317     case JavaType::TypeString: {
    318       ScopedJavaLocalRef<jclass> clazz(
    319           base::android::GetClass(env, kJavaLangString));
    320       return env->NewObjectArray(length, clazz.obj(), NULL);
    321     }
    322     case JavaType::TypeVoid:
    323       // Conversion to void must never happen.
    324     case JavaType::TypeArray:
    325     case JavaType::TypeObject:
    326       // Not handled.
    327       NOTREACHED();
    328   }
    329   return NULL;
    330 }
    331 
    332 // Sets the specified element of the supplied array to the value of the
    333 // supplied jvalue. Requires that the type of the array matches that of the
    334 // jvalue. Handles only primitive types and strings. Note that in the case of a
    335 // string, the array takes a new reference to the string object.
    336 void SetArrayElement(JNIEnv* env,
    337                      jobject array,
    338                      const JavaType& type,
    339                      jsize index,
    340                      const jvalue& value) {
    341   switch (type.type) {
    342     case JavaType::TypeBoolean:
    343       env->SetBooleanArrayRegion(static_cast<jbooleanArray>(array), index, 1,
    344                                  &value.z);
    345       break;
    346     case JavaType::TypeByte:
    347       env->SetByteArrayRegion(static_cast<jbyteArray>(array), index, 1,
    348                               &value.b);
    349       break;
    350     case JavaType::TypeChar:
    351       env->SetCharArrayRegion(static_cast<jcharArray>(array), index, 1,
    352                               &value.c);
    353       break;
    354     case JavaType::TypeShort:
    355       env->SetShortArrayRegion(static_cast<jshortArray>(array), index, 1,
    356                                &value.s);
    357       break;
    358     case JavaType::TypeInt:
    359       env->SetIntArrayRegion(static_cast<jintArray>(array), index, 1,
    360                              &value.i);
    361       break;
    362     case JavaType::TypeLong:
    363       env->SetLongArrayRegion(static_cast<jlongArray>(array), index, 1,
    364                               &value.j);
    365       break;
    366     case JavaType::TypeFloat:
    367       env->SetFloatArrayRegion(static_cast<jfloatArray>(array), index, 1,
    368                                &value.f);
    369       break;
    370     case JavaType::TypeDouble:
    371       env->SetDoubleArrayRegion(static_cast<jdoubleArray>(array), index, 1,
    372                                 &value.d);
    373       break;
    374     case JavaType::TypeString:
    375       env->SetObjectArrayElement(static_cast<jobjectArray>(array), index,
    376                                  value.l);
    377       break;
    378     case JavaType::TypeVoid:
    379       // Conversion to void must never happen.
    380     case JavaType::TypeArray:
    381     case JavaType::TypeObject:
    382       // Not handled.
    383       NOTREACHED();
    384   }
    385   base::android::CheckException(env);
    386 }
    387 
    388 jvalue CoerceJavaScriptNullOrUndefinedToJavaValue(JNIEnv* env,
    389                                                   const base::Value* value,
    390                                                   const JavaType& target_type,
    391                                                   bool coerce_to_string,
    392                                                   GinJavaBridgeError* error) {
    393   bool is_undefined = false;
    394   scoped_ptr<const GinJavaBridgeValue> gin_value;
    395   if (GinJavaBridgeValue::ContainsGinJavaBridgeValue(value)) {
    396     gin_value = GinJavaBridgeValue::FromValue(value);
    397     if (gin_value->IsType(GinJavaBridgeValue::TYPE_UNDEFINED)) {
    398       is_undefined = true;
    399     }
    400   }
    401   jvalue result;
    402   switch (target_type.type) {
    403     case JavaType::TypeObject:
    404       result.l = NULL;
    405       break;
    406     case JavaType::TypeString:
    407       // LIVECONNECT_COMPLIANCE: Existing behavior is to convert undefined to
    408       // "undefined". Spec requires converting undefined to NULL.
    409       result.l = (coerce_to_string && is_undefined)
    410                      ? ConvertUTF8ToJString(env, kUndefined)
    411                      : NULL;
    412       break;
    413     case JavaType::TypeByte:
    414     case JavaType::TypeChar:
    415     case JavaType::TypeShort:
    416     case JavaType::TypeInt:
    417     case JavaType::TypeLong:
    418     case JavaType::TypeFloat:
    419     case JavaType::TypeDouble: {
    420       jvalue null_value = {0};
    421       result = null_value;
    422       break;
    423     }
    424     case JavaType::TypeBoolean:
    425       result.z = JNI_FALSE;
    426       break;
    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 jobject CoerceJavaScriptListToArray(JNIEnv* env,
    441                                     const base::Value* value,
    442                                     const JavaType& target_type,
    443                                     const ObjectRefs& object_refs,
    444                                     GinJavaBridgeError* error) {
    445   DCHECK_EQ(JavaType::TypeArray, target_type.type);
    446   const JavaType& target_inner_type = *target_type.inner_type.get();
    447   // LIVECONNECT_COMPLIANCE: Existing behavior is to return null for
    448   // multi-dimensional arrays. Spec requires handling multi-demensional arrays.
    449   if (target_inner_type.type == JavaType::TypeArray) {
    450     return NULL;
    451   }
    452 
    453   // LIVECONNECT_COMPLIANCE: Existing behavior is to return null for object
    454   // arrays. Spec requires handling object arrays.
    455   if (target_inner_type.type == JavaType::TypeObject) {
    456     return NULL;
    457   }
    458 
    459   const base::ListValue* list_value;
    460   value->GetAsList(&list_value);
    461   // Create the Java array.
    462   jsize length = static_cast<jsize>(list_value->GetSize());
    463   jobject result = CreateJavaArray(env, target_inner_type, length);
    464   if (!result) {
    465     return NULL;
    466   }
    467   scoped_ptr<base::Value> null_value(base::Value::CreateNullValue());
    468   for (jsize i = 0; i < length; ++i) {
    469     const base::Value* value_element = null_value.get();
    470     list_value->Get(i, &value_element);
    471     jvalue element = CoerceJavaScriptValueToJavaValue(
    472         env, value_element, target_inner_type, false, object_refs, error);
    473     SetArrayElement(env, result, target_inner_type, i, element);
    474     // CoerceJavaScriptValueToJavaValue() creates new local references to
    475     // strings, objects and arrays. Of these, only strings can occur here.
    476     // SetArrayElement() causes the array to take its own reference to the
    477     // string, so we can now release the local reference.
    478     DCHECK_NE(JavaType::TypeObject, target_inner_type.type);
    479     DCHECK_NE(JavaType::TypeArray, target_inner_type.type);
    480     ReleaseJavaValueIfRequired(env, &element, target_inner_type);
    481   }
    482 
    483   return result;
    484 }
    485 
    486 jobject CoerceJavaScriptDictionaryToArray(JNIEnv* env,
    487                                           const base::Value* value,
    488                                           const JavaType& target_type,
    489                                           const ObjectRefs& object_refs,
    490                                           GinJavaBridgeError* error) {
    491   DCHECK_EQ(JavaType::TypeArray, target_type.type);
    492 
    493   const JavaType& target_inner_type = *target_type.inner_type.get();
    494   // LIVECONNECT_COMPLIANCE: Existing behavior is to return null for
    495   // multi-dimensional arrays. Spec requires handling multi-demensional arrays.
    496   if (target_inner_type.type == JavaType::TypeArray) {
    497     return NULL;
    498   }
    499 
    500   // LIVECONNECT_COMPLIANCE: Existing behavior is to return null for object
    501   // arrays. Spec requires handling object arrays.
    502   if (target_inner_type.type == JavaType::TypeObject) {
    503     return NULL;
    504   }
    505 
    506   const base::DictionaryValue* dictionary_value;
    507   value->GetAsDictionary(&dictionary_value);
    508   const base::Value* length_value;
    509   // If the object does not have a length property, return null.
    510   if (!dictionary_value->Get("length", &length_value)) {
    511     return NULL;
    512   }
    513 
    514   // If the length property does not have numeric type, or is outside the valid
    515   // range for a Java array length, return null.
    516   jsize length = -1;
    517   if (length_value->IsType(base::Value::TYPE_INTEGER)) {
    518     int int_length;
    519     length_value->GetAsInteger(&int_length);
    520     if (int_length >= 0 && int_length <= kint32max) {
    521       length = static_cast<jsize>(int_length);
    522     }
    523   } else if (length_value->IsType(base::Value::TYPE_DOUBLE)) {
    524     double double_length;
    525     length_value->GetAsDouble(&double_length);
    526     if (double_length >= 0.0 && double_length <= kint32max) {
    527       length = static_cast<jsize>(double_length);
    528     }
    529   }
    530   if (length == -1) {
    531     return NULL;
    532   }
    533 
    534   jobject result = CreateJavaArray(env, target_inner_type, length);
    535   if (!result) {
    536     return NULL;
    537   }
    538   scoped_ptr<base::Value> null_value(base::Value::CreateNullValue());
    539   for (jsize i = 0; i < length; ++i) {
    540     const std::string key(base::IntToString(i));
    541     const base::Value* value_element = null_value.get();
    542     if (dictionary_value->HasKey(key)) {
    543       dictionary_value->Get(key, &value_element);
    544     }
    545     jvalue element = CoerceJavaScriptValueToJavaValue(
    546         env, value_element, target_inner_type, false, object_refs, error);
    547     SetArrayElement(env, result, target_inner_type, i, element);
    548     // CoerceJavaScriptValueToJavaValue() creates new local references to
    549     // strings, objects and arrays. Of these, only strings can occur here.
    550     // SetArrayElement() causes the array to take its own reference to the
    551     // string, so we can now release the local reference.
    552     DCHECK_NE(JavaType::TypeObject, target_inner_type.type);
    553     DCHECK_NE(JavaType::TypeArray, target_inner_type.type);
    554     ReleaseJavaValueIfRequired(env, &element, target_inner_type);
    555   }
    556 
    557   return result;
    558 }
    559 
    560 // Returns 'true' if it is possible to cast an object of class |src| to
    561 // an object of class |dst|.
    562 bool CanAssignClassVariables(JNIEnv* env,
    563                              const ScopedJavaLocalRef<jclass>& dst,
    564                              const ScopedJavaLocalRef<jclass>& src) {
    565   if (dst.is_null() || src.is_null())
    566     return false;
    567   return env->IsAssignableFrom(src.obj(), dst.obj()) == JNI_TRUE;
    568 }
    569 
    570 ScopedJavaLocalRef<jclass> GetObjectClass(
    571     JNIEnv* env,
    572     const ScopedJavaLocalRef<jobject>& obj) {
    573   jclass clazz = env->GetObjectClass(obj.obj());
    574   return ScopedJavaLocalRef<jclass>(env, clazz);
    575 }
    576 
    577 jvalue CoerceJavaScriptObjectToJavaValue(JNIEnv* env,
    578                                          const base::Value* value,
    579                                          const JavaType& target_type,
    580                                          bool coerce_to_string,
    581                                          const ObjectRefs& object_refs,
    582                                          GinJavaBridgeError* error) {
    583   // This covers both JavaScript objects (including arrays) and Java objects.
    584   // See http://jdk6.java.net/plugin2/liveconnect/#JS_OTHER_OBJECTS,
    585   // http://jdk6.java.net/plugin2/liveconnect/#JS_ARRAY_VALUES and
    586   // http://jdk6.java.net/plugin2/liveconnect/#JS_JAVA_OBJECTS
    587   jvalue result;
    588   switch (target_type.type) {
    589     case JavaType::TypeObject: {
    590       if (GinJavaBridgeValue::ContainsGinJavaBridgeValue(value)) {
    591         scoped_ptr<const GinJavaBridgeValue> gin_value(
    592             GinJavaBridgeValue::FromValue(value));
    593         DCHECK(gin_value);
    594         DCHECK(gin_value->IsType(GinJavaBridgeValue::TYPE_OBJECT_ID));
    595         ScopedJavaLocalRef<jobject> obj;
    596         GinJavaBoundObject::ObjectID object_id;
    597         if (gin_value->GetAsObjectID(&object_id)) {
    598           ObjectRefs::const_iterator iter = object_refs.find(object_id);
    599           if (iter != object_refs.end()) {
    600             obj.Reset(iter->second.get(env));
    601           }
    602         }
    603         DCHECK(!target_type.class_jni_name.empty());
    604         DCHECK(!obj.is_null());
    605         if (CanAssignClassVariables(
    606                 env,
    607                 base::android::GetClass(env, target_type.JNIName().c_str()),
    608                 GetObjectClass(env, obj))) {
    609           result.l = obj.Release();
    610         } else {
    611           result.l = NULL;
    612           *error = kGinJavaBridgeNonAssignableTypes;
    613         }
    614       } else {
    615         // LIVECONNECT_COMPLIANCE: Existing behavior is to pass null. Spec
    616         // requires converting if the target type is
    617         // netscape.javascript.JSObject, otherwise raising a JavaScript
    618         // exception.
    619         result.l = NULL;
    620       }
    621       break;
    622     }
    623     case JavaType::TypeString:
    624       // LIVECONNECT_COMPLIANCE: Existing behavior is to convert to
    625       // "undefined". Spec requires calling toString() on the Java object.
    626       result.l =
    627           coerce_to_string ? ConvertUTF8ToJString(env, kUndefined) : NULL;
    628       break;
    629     case JavaType::TypeByte:
    630     case JavaType::TypeShort:
    631     case JavaType::TypeInt:
    632     case JavaType::TypeLong:
    633     case JavaType::TypeFloat:
    634     case JavaType::TypeDouble:
    635     case JavaType::TypeChar: {
    636       // LIVECONNECT_COMPLIANCE: Existing behavior is to convert to 0. Spec
    637       // requires raising a JavaScript exception.
    638       jvalue null_value = {0};
    639       result = null_value;
    640       break;
    641     }
    642     case JavaType::TypeBoolean:
    643       // LIVECONNECT_COMPLIANCE: Existing behavior is to convert to false. Spec
    644       // requires raising a JavaScript exception.
    645       result.z = JNI_FALSE;
    646       break;
    647     case JavaType::TypeArray:
    648       if (value->IsType(base::Value::TYPE_DICTIONARY)) {
    649         result.l = CoerceJavaScriptDictionaryToArray(
    650             env, value, target_type, object_refs, error);
    651       } else if (value->IsType(base::Value::TYPE_LIST)) {
    652         result.l = CoerceJavaScriptListToArray(
    653             env, value, target_type, object_refs, error);
    654       } else {
    655         result.l = NULL;
    656       }
    657       break;
    658     case JavaType::TypeVoid:
    659       // Conversion to void must never happen.
    660       NOTREACHED();
    661       break;
    662   }
    663   return result;
    664 }
    665 
    666 jvalue CoerceGinJavaBridgeValueToJavaValue(JNIEnv* env,
    667                                            const base::Value* value,
    668                                            const JavaType& target_type,
    669                                            bool coerce_to_string,
    670                                            const ObjectRefs& object_refs,
    671                                            GinJavaBridgeError* error) {
    672   DCHECK(GinJavaBridgeValue::ContainsGinJavaBridgeValue(value));
    673   scoped_ptr<const GinJavaBridgeValue> gin_value(
    674       GinJavaBridgeValue::FromValue(value));
    675   switch (gin_value->GetType()) {
    676     case GinJavaBridgeValue::TYPE_UNDEFINED:
    677       return CoerceJavaScriptNullOrUndefinedToJavaValue(
    678           env, value, target_type, coerce_to_string, error);
    679     case GinJavaBridgeValue::TYPE_NONFINITE: {
    680       float float_value;
    681       gin_value->GetAsNonFinite(&float_value);
    682       return CoerceJavaScriptDoubleToJavaValue(
    683           env, float_value, target_type, coerce_to_string, error);
    684     }
    685     case GinJavaBridgeValue::TYPE_OBJECT_ID:
    686       return CoerceJavaScriptObjectToJavaValue(
    687           env, value, target_type, coerce_to_string, object_refs, error);
    688     default:
    689       NOTREACHED();
    690   }
    691   return jvalue();
    692 }
    693 
    694 }  // namespace
    695 
    696 
    697 void ReleaseJavaValueIfRequired(JNIEnv* env,
    698                                 jvalue* value,
    699                                 const JavaType& type) {
    700   if (type.type == JavaType::TypeString || type.type == JavaType::TypeObject ||
    701       type.type == JavaType::TypeArray) {
    702     env->DeleteLocalRef(value->l);
    703     value->l = NULL;
    704   }
    705 }
    706 
    707 jvalue CoerceJavaScriptValueToJavaValue(JNIEnv* env,
    708                                         const base::Value* value,
    709                                         const JavaType& target_type,
    710                                         bool coerce_to_string,
    711                                         const ObjectRefs& object_refs,
    712                                         GinJavaBridgeError* error) {
    713   // Note that in all these conversions, the relevant field of the jvalue must
    714   // always be explicitly set, as jvalue does not initialize its fields.
    715 
    716   switch (value->GetType()) {
    717     case base::Value::TYPE_INTEGER:
    718       return CoerceJavaScriptIntegerToJavaValue(
    719           env, value, target_type, coerce_to_string, error);
    720     case base::Value::TYPE_DOUBLE: {
    721       double double_value;
    722       value->GetAsDouble(&double_value);
    723       return CoerceJavaScriptDoubleToJavaValue(
    724           env, double_value, target_type, coerce_to_string, error);
    725     }
    726     case base::Value::TYPE_BOOLEAN:
    727       return CoerceJavaScriptBooleanToJavaValue(
    728           env, value, target_type, coerce_to_string, error);
    729     case base::Value::TYPE_STRING:
    730       return CoerceJavaScriptStringToJavaValue(env, value, target_type, error);
    731     case base::Value::TYPE_DICTIONARY:
    732     case base::Value::TYPE_LIST:
    733       return CoerceJavaScriptObjectToJavaValue(
    734           env, value, target_type, coerce_to_string, object_refs, error);
    735     case base::Value::TYPE_NULL:
    736       return CoerceJavaScriptNullOrUndefinedToJavaValue(
    737           env, value, target_type, coerce_to_string, error);
    738     case base::Value::TYPE_BINARY:
    739       return CoerceGinJavaBridgeValueToJavaValue(
    740           env, value, target_type, coerce_to_string, object_refs, error);
    741   }
    742   NOTREACHED();
    743   return jvalue();
    744 }
    745 
    746 }  // namespace content
    747