| // Copyright 2014 The Chromium Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #include "content/browser/android/java/gin_java_script_to_java_types_coercion.h" |
| |
| #include <unistd.h> |
| |
| #include "base/android/jni_android.h" |
| #include "base/android/jni_string.h" |
| #include "base/strings/string_number_conversions.h" |
| #include "base/strings/stringprintf.h" |
| #include "base/strings/utf_string_conversions.h" |
| #include "content/common/android/gin_java_bridge_value.h" |
| #include "third_party/WebKit/public/platform/WebString.h" |
| |
| using base::android::ScopedJavaLocalRef; |
| |
| namespace content { |
| |
| namespace { |
| |
| const char kJavaLangString[] = "java/lang/String"; |
| const char kUndefined[] = "undefined"; |
| |
| // This is an intermediate solution until we fix http://crbug.com/391492. |
| jstring ConvertUTF8ToJString(JNIEnv* env, const std::string& string) { |
| base::string16 utf16( |
| blink::WebString::fromUTF8(string.c_str(), string.size())); |
| return env->NewString(utf16.data(), utf16.length()); |
| } |
| |
| double RoundDoubleTowardsZero(const double& x) { |
| if (std::isnan(x)) { |
| return 0.0; |
| } |
| return x > 0.0 ? floor(x) : ceil(x); |
| } |
| |
| // Rounds to jlong using Java's type conversion rules. |
| jlong RoundDoubleToLong(const double& x) { |
| double intermediate = RoundDoubleTowardsZero(x); |
| // The int64 limits can not be converted exactly to double values, so we |
| // compare to custom constants. kint64max is 2^63 - 1, but the spacing |
| // between double values in the the range 2^62 to 2^63 is 2^10. The cast is |
| // required to silence a spurious gcc warning for integer overflow. |
| const int64 kLimit = (GG_INT64_C(1) << 63) - static_cast<uint64>(1 << 10); |
| DCHECK(kLimit > 0); |
| const double kLargestDoubleLessThanInt64Max = kLimit; |
| const double kSmallestDoubleGreaterThanInt64Min = -kLimit; |
| if (intermediate > kLargestDoubleLessThanInt64Max) { |
| return kint64max; |
| } |
| if (intermediate < kSmallestDoubleGreaterThanInt64Min) { |
| return kint64min; |
| } |
| return static_cast<jlong>(intermediate); |
| } |
| |
| // Rounds to jint using Java's type conversion rules. |
| jint RoundDoubleToInt(const double& x) { |
| double intermediate = RoundDoubleTowardsZero(x); |
| // The int32 limits cast exactly to double values. |
| intermediate = std::min(intermediate, static_cast<double>(kint32max)); |
| intermediate = std::max(intermediate, static_cast<double>(kint32min)); |
| return static_cast<jint>(intermediate); |
| } |
| |
| jvalue CoerceJavaScriptIntegerToJavaValue(JNIEnv* env, |
| const base::Value* value, |
| const JavaType& target_type, |
| bool coerce_to_string, |
| GinJavaBridgeError* error) { |
| // See http://jdk6.java.net/plugin2/liveconnect/#JS_NUMBER_VALUES. |
| |
| // For conversion to numeric types, we need to replicate Java's type |
| // conversion rules. This requires that for integer values, we simply discard |
| // all but the lowest n buts, where n is the number of bits in the target |
| // type. |
| jvalue result; |
| int int_value; |
| value->GetAsInteger(&int_value); |
| switch (target_type.type) { |
| case JavaType::TypeByte: |
| result.b = static_cast<jbyte>(int_value); |
| break; |
| case JavaType::TypeChar: |
| result.c = static_cast<jchar>(int_value); |
| break; |
| case JavaType::TypeShort: |
| result.s = static_cast<jshort>(int_value); |
| break; |
| case JavaType::TypeInt: |
| result.i = int_value; |
| break; |
| case JavaType::TypeLong: |
| result.j = int_value; |
| break; |
| case JavaType::TypeFloat: |
| result.f = int_value; |
| break; |
| case JavaType::TypeDouble: |
| result.d = int_value; |
| break; |
| case JavaType::TypeObject: |
| // LIVECONNECT_COMPLIANCE: Existing behavior is to convert to null. Spec |
| // requires handling object equivalents of primitive types. |
| result.l = NULL; |
| break; |
| case JavaType::TypeString: |
| result.l = coerce_to_string |
| ? ConvertUTF8ToJString(env, base::Int64ToString(int_value)) |
| : NULL; |
| break; |
| case JavaType::TypeBoolean: |
| // LIVECONNECT_COMPLIANCE: Existing behavior is to convert to false. Spec |
| // requires converting to false for 0 or NaN, true otherwise. |
| result.z = JNI_FALSE; |
| break; |
| case JavaType::TypeArray: |
| // LIVECONNECT_COMPLIANCE: Existing behavior is to convert to null. Spec |
| // requires raising a JavaScript exception. |
| result.l = NULL; |
| break; |
| case JavaType::TypeVoid: |
| // Conversion to void must never happen. |
| NOTREACHED(); |
| break; |
| } |
| return result; |
| } |
| |
| jvalue CoerceJavaScriptDoubleToJavaValue(JNIEnv* env, |
| double double_value, |
| const JavaType& target_type, |
| bool coerce_to_string, |
| GinJavaBridgeError* error) { |
| // See http://jdk6.java.net/plugin2/liveconnect/#JS_NUMBER_VALUES. |
| // For conversion to numeric types, we need to replicate Java's type |
| // conversion rules. |
| jvalue result; |
| switch (target_type.type) { |
| case JavaType::TypeByte: |
| result.b = static_cast<jbyte>(RoundDoubleToInt(double_value)); |
| break; |
| case JavaType::TypeChar: |
| // LIVECONNECT_COMPLIANCE: Existing behavior is to convert double to 0. |
| // Spec requires converting doubles similarly to how we convert doubles to |
| // other numeric types. |
| result.c = 0; |
| break; |
| case JavaType::TypeShort: |
| result.s = static_cast<jshort>(RoundDoubleToInt(double_value)); |
| break; |
| case JavaType::TypeInt: |
| result.i = RoundDoubleToInt(double_value); |
| break; |
| case JavaType::TypeLong: |
| result.j = RoundDoubleToLong(double_value); |
| break; |
| case JavaType::TypeFloat: |
| result.f = static_cast<jfloat>(double_value); |
| break; |
| case JavaType::TypeDouble: |
| result.d = double_value; |
| break; |
| case JavaType::TypeObject: |
| // LIVECONNECT_COMPLIANCE: Existing behavior is to convert to null. Spec |
| // requires handling object equivalents of primitive types. |
| result.l = NULL; |
| break; |
| case JavaType::TypeString: |
| result.l = coerce_to_string |
| ? ConvertUTF8ToJString( |
| env, base::StringPrintf("%.6lg", double_value)) |
| : NULL; |
| break; |
| case JavaType::TypeBoolean: |
| // LIVECONNECT_COMPLIANCE: Existing behavior is to convert to false. Spec |
| // requires converting to false for 0 or NaN, true otherwise. |
| result.z = JNI_FALSE; |
| break; |
| case JavaType::TypeArray: |
| // LIVECONNECT_COMPLIANCE: Existing behavior is to convert to null. Spec |
| // requires raising a JavaScript exception. |
| result.l = NULL; |
| break; |
| case JavaType::TypeVoid: |
| // Conversion to void must never happen. |
| NOTREACHED(); |
| break; |
| } |
| return result; |
| } |
| |
| jvalue CoerceJavaScriptBooleanToJavaValue(JNIEnv* env, |
| const base::Value* value, |
| const JavaType& target_type, |
| bool coerce_to_string, |
| GinJavaBridgeError* error) { |
| // See http://jdk6.java.net/plugin2/liveconnect/#JS_BOOLEAN_VALUES. |
| bool boolean_value; |
| value->GetAsBoolean(&boolean_value); |
| jvalue result; |
| switch (target_type.type) { |
| case JavaType::TypeBoolean: |
| result.z = boolean_value ? JNI_TRUE : JNI_FALSE; |
| break; |
| case JavaType::TypeObject: |
| // LIVECONNECT_COMPLIANCE: Existing behavior is to convert to NULL. Spec |
| // requires handling java.lang.Boolean and java.lang.Object. |
| result.l = NULL; |
| break; |
| case JavaType::TypeString: |
| result.l = coerce_to_string ? ConvertUTF8ToJString( |
| env, boolean_value ? "true" : "false") |
| : NULL; |
| break; |
| case JavaType::TypeByte: |
| case JavaType::TypeChar: |
| case JavaType::TypeShort: |
| case JavaType::TypeInt: |
| case JavaType::TypeLong: |
| case JavaType::TypeFloat: |
| case JavaType::TypeDouble: { |
| // LIVECONNECT_COMPLIANCE: Existing behavior is to convert to 0. Spec |
| // requires converting to 0 or 1. |
| jvalue null_value = {0}; |
| result = null_value; |
| break; |
| } |
| case JavaType::TypeArray: |
| // LIVECONNECT_COMPLIANCE: Existing behavior is to convert to NULL. Spec |
| // requires raising a JavaScript exception. |
| result.l = NULL; |
| break; |
| case JavaType::TypeVoid: |
| // Conversion to void must never happen. |
| NOTREACHED(); |
| break; |
| } |
| return result; |
| } |
| |
| jvalue CoerceJavaScriptStringToJavaValue(JNIEnv* env, |
| const base::Value* value, |
| const JavaType& target_type, |
| GinJavaBridgeError* error) { |
| // See http://jdk6.java.net/plugin2/liveconnect/#JS_STRING_VALUES. |
| jvalue result; |
| switch (target_type.type) { |
| case JavaType::TypeString: { |
| std::string string_result; |
| value->GetAsString(&string_result); |
| result.l = ConvertUTF8ToJString(env, string_result); |
| break; |
| } |
| case JavaType::TypeObject: |
| // LIVECONNECT_COMPLIANCE: Existing behavior is to convert to NULL. Spec |
| // requires handling java.lang.Object. |
| result.l = NULL; |
| break; |
| case JavaType::TypeByte: |
| case JavaType::TypeShort: |
| case JavaType::TypeInt: |
| case JavaType::TypeLong: |
| case JavaType::TypeFloat: |
| case JavaType::TypeDouble: { |
| // LIVECONNECT_COMPLIANCE: Existing behavior is to convert to 0. Spec |
| // requires using valueOf() method of corresponding object type. |
| jvalue null_value = {0}; |
| result = null_value; |
| break; |
| } |
| case JavaType::TypeChar: |
| // LIVECONNECT_COMPLIANCE: Existing behavior is to convert to 0. Spec |
| // requires using java.lang.Short.decode(). |
| result.c = 0; |
| break; |
| case JavaType::TypeBoolean: |
| // LIVECONNECT_COMPLIANCE: Existing behavior is to convert to false. Spec |
| // requires converting the empty string to false, otherwise true. |
| result.z = JNI_FALSE; |
| break; |
| case JavaType::TypeArray: |
| // LIVECONNECT_COMPLIANCE: Existing behavior is to convert to NULL. Spec |
| // requires raising a JavaScript exception. |
| result.l = NULL; |
| break; |
| case JavaType::TypeVoid: |
| // Conversion to void must never happen. |
| NOTREACHED(); |
| break; |
| } |
| return result; |
| } |
| |
| // Note that this only handles primitive types and strings. |
| jobject CreateJavaArray(JNIEnv* env, const JavaType& type, jsize length) { |
| switch (type.type) { |
| case JavaType::TypeBoolean: |
| return env->NewBooleanArray(length); |
| case JavaType::TypeByte: |
| return env->NewByteArray(length); |
| case JavaType::TypeChar: |
| return env->NewCharArray(length); |
| case JavaType::TypeShort: |
| return env->NewShortArray(length); |
| case JavaType::TypeInt: |
| return env->NewIntArray(length); |
| case JavaType::TypeLong: |
| return env->NewLongArray(length); |
| case JavaType::TypeFloat: |
| return env->NewFloatArray(length); |
| case JavaType::TypeDouble: |
| return env->NewDoubleArray(length); |
| case JavaType::TypeString: { |
| ScopedJavaLocalRef<jclass> clazz( |
| base::android::GetClass(env, kJavaLangString)); |
| return env->NewObjectArray(length, clazz.obj(), NULL); |
| } |
| case JavaType::TypeVoid: |
| // Conversion to void must never happen. |
| case JavaType::TypeArray: |
| case JavaType::TypeObject: |
| // Not handled. |
| NOTREACHED(); |
| } |
| return NULL; |
| } |
| |
| // Sets the specified element of the supplied array to the value of the |
| // supplied jvalue. Requires that the type of the array matches that of the |
| // jvalue. Handles only primitive types and strings. Note that in the case of a |
| // string, the array takes a new reference to the string object. |
| void SetArrayElement(JNIEnv* env, |
| jobject array, |
| const JavaType& type, |
| jsize index, |
| const jvalue& value) { |
| switch (type.type) { |
| case JavaType::TypeBoolean: |
| env->SetBooleanArrayRegion(static_cast<jbooleanArray>(array), index, 1, |
| &value.z); |
| break; |
| case JavaType::TypeByte: |
| env->SetByteArrayRegion(static_cast<jbyteArray>(array), index, 1, |
| &value.b); |
| break; |
| case JavaType::TypeChar: |
| env->SetCharArrayRegion(static_cast<jcharArray>(array), index, 1, |
| &value.c); |
| break; |
| case JavaType::TypeShort: |
| env->SetShortArrayRegion(static_cast<jshortArray>(array), index, 1, |
| &value.s); |
| break; |
| case JavaType::TypeInt: |
| env->SetIntArrayRegion(static_cast<jintArray>(array), index, 1, |
| &value.i); |
| break; |
| case JavaType::TypeLong: |
| env->SetLongArrayRegion(static_cast<jlongArray>(array), index, 1, |
| &value.j); |
| break; |
| case JavaType::TypeFloat: |
| env->SetFloatArrayRegion(static_cast<jfloatArray>(array), index, 1, |
| &value.f); |
| break; |
| case JavaType::TypeDouble: |
| env->SetDoubleArrayRegion(static_cast<jdoubleArray>(array), index, 1, |
| &value.d); |
| break; |
| case JavaType::TypeString: |
| env->SetObjectArrayElement(static_cast<jobjectArray>(array), index, |
| value.l); |
| break; |
| case JavaType::TypeVoid: |
| // Conversion to void must never happen. |
| case JavaType::TypeArray: |
| case JavaType::TypeObject: |
| // Not handled. |
| NOTREACHED(); |
| } |
| base::android::CheckException(env); |
| } |
| |
| jvalue CoerceJavaScriptNullOrUndefinedToJavaValue(JNIEnv* env, |
| const base::Value* value, |
| const JavaType& target_type, |
| bool coerce_to_string, |
| GinJavaBridgeError* error) { |
| bool is_undefined = false; |
| scoped_ptr<const GinJavaBridgeValue> gin_value; |
| if (GinJavaBridgeValue::ContainsGinJavaBridgeValue(value)) { |
| gin_value = GinJavaBridgeValue::FromValue(value); |
| if (gin_value->IsType(GinJavaBridgeValue::TYPE_UNDEFINED)) { |
| is_undefined = true; |
| } |
| } |
| jvalue result; |
| switch (target_type.type) { |
| case JavaType::TypeObject: |
| result.l = NULL; |
| break; |
| case JavaType::TypeString: |
| // LIVECONNECT_COMPLIANCE: Existing behavior is to convert undefined to |
| // "undefined". Spec requires converting undefined to NULL. |
| result.l = (coerce_to_string && is_undefined) |
| ? ConvertUTF8ToJString(env, kUndefined) |
| : NULL; |
| break; |
| case JavaType::TypeByte: |
| case JavaType::TypeChar: |
| case JavaType::TypeShort: |
| case JavaType::TypeInt: |
| case JavaType::TypeLong: |
| case JavaType::TypeFloat: |
| case JavaType::TypeDouble: { |
| jvalue null_value = {0}; |
| result = null_value; |
| break; |
| } |
| case JavaType::TypeBoolean: |
| result.z = JNI_FALSE; |
| break; |
| case JavaType::TypeArray: |
| // LIVECONNECT_COMPLIANCE: Existing behavior is to convert to NULL. Spec |
| // requires raising a JavaScript exception. |
| result.l = NULL; |
| break; |
| case JavaType::TypeVoid: |
| // Conversion to void must never happen. |
| NOTREACHED(); |
| break; |
| } |
| return result; |
| } |
| |
| jobject CoerceJavaScriptListToArray(JNIEnv* env, |
| const base::Value* value, |
| const JavaType& target_type, |
| const ObjectRefs& object_refs, |
| GinJavaBridgeError* error) { |
| DCHECK_EQ(JavaType::TypeArray, target_type.type); |
| const JavaType& target_inner_type = *target_type.inner_type.get(); |
| // LIVECONNECT_COMPLIANCE: Existing behavior is to return null for |
| // multi-dimensional arrays. Spec requires handling multi-demensional arrays. |
| if (target_inner_type.type == JavaType::TypeArray) { |
| return NULL; |
| } |
| |
| // LIVECONNECT_COMPLIANCE: Existing behavior is to return null for object |
| // arrays. Spec requires handling object arrays. |
| if (target_inner_type.type == JavaType::TypeObject) { |
| return NULL; |
| } |
| |
| const base::ListValue* list_value; |
| value->GetAsList(&list_value); |
| // Create the Java array. |
| jsize length = static_cast<jsize>(list_value->GetSize()); |
| jobject result = CreateJavaArray(env, target_inner_type, length); |
| if (!result) { |
| return NULL; |
| } |
| scoped_ptr<base::Value> null_value(base::Value::CreateNullValue()); |
| for (jsize i = 0; i < length; ++i) { |
| const base::Value* value_element = null_value.get(); |
| list_value->Get(i, &value_element); |
| jvalue element = CoerceJavaScriptValueToJavaValue( |
| env, value_element, target_inner_type, false, object_refs, error); |
| SetArrayElement(env, result, target_inner_type, i, element); |
| // CoerceJavaScriptValueToJavaValue() creates new local references to |
| // strings, objects and arrays. Of these, only strings can occur here. |
| // SetArrayElement() causes the array to take its own reference to the |
| // string, so we can now release the local reference. |
| DCHECK_NE(JavaType::TypeObject, target_inner_type.type); |
| DCHECK_NE(JavaType::TypeArray, target_inner_type.type); |
| ReleaseJavaValueIfRequired(env, &element, target_inner_type); |
| } |
| |
| return result; |
| } |
| |
| jobject CoerceJavaScriptDictionaryToArray(JNIEnv* env, |
| const base::Value* value, |
| const JavaType& target_type, |
| const ObjectRefs& object_refs, |
| GinJavaBridgeError* error) { |
| DCHECK_EQ(JavaType::TypeArray, target_type.type); |
| |
| const JavaType& target_inner_type = *target_type.inner_type.get(); |
| // LIVECONNECT_COMPLIANCE: Existing behavior is to return null for |
| // multi-dimensional arrays. Spec requires handling multi-demensional arrays. |
| if (target_inner_type.type == JavaType::TypeArray) { |
| return NULL; |
| } |
| |
| // LIVECONNECT_COMPLIANCE: Existing behavior is to return null for object |
| // arrays. Spec requires handling object arrays. |
| if (target_inner_type.type == JavaType::TypeObject) { |
| return NULL; |
| } |
| |
| const base::DictionaryValue* dictionary_value; |
| value->GetAsDictionary(&dictionary_value); |
| const base::Value* length_value; |
| // If the object does not have a length property, return null. |
| if (!dictionary_value->Get("length", &length_value)) { |
| return NULL; |
| } |
| |
| // If the length property does not have numeric type, or is outside the valid |
| // range for a Java array length, return null. |
| jsize length = -1; |
| if (length_value->IsType(base::Value::TYPE_INTEGER)) { |
| int int_length; |
| length_value->GetAsInteger(&int_length); |
| if (int_length >= 0 && int_length <= kint32max) { |
| length = static_cast<jsize>(int_length); |
| } |
| } else if (length_value->IsType(base::Value::TYPE_DOUBLE)) { |
| double double_length; |
| length_value->GetAsDouble(&double_length); |
| if (double_length >= 0.0 && double_length <= kint32max) { |
| length = static_cast<jsize>(double_length); |
| } |
| } |
| if (length == -1) { |
| return NULL; |
| } |
| |
| jobject result = CreateJavaArray(env, target_inner_type, length); |
| if (!result) { |
| return NULL; |
| } |
| scoped_ptr<base::Value> null_value(base::Value::CreateNullValue()); |
| for (jsize i = 0; i < length; ++i) { |
| const std::string key(base::IntToString(i)); |
| const base::Value* value_element = null_value.get(); |
| if (dictionary_value->HasKey(key)) { |
| dictionary_value->Get(key, &value_element); |
| } |
| jvalue element = CoerceJavaScriptValueToJavaValue( |
| env, value_element, target_inner_type, false, object_refs, error); |
| SetArrayElement(env, result, target_inner_type, i, element); |
| // CoerceJavaScriptValueToJavaValue() creates new local references to |
| // strings, objects and arrays. Of these, only strings can occur here. |
| // SetArrayElement() causes the array to take its own reference to the |
| // string, so we can now release the local reference. |
| DCHECK_NE(JavaType::TypeObject, target_inner_type.type); |
| DCHECK_NE(JavaType::TypeArray, target_inner_type.type); |
| ReleaseJavaValueIfRequired(env, &element, target_inner_type); |
| } |
| |
| return result; |
| } |
| |
| // Returns 'true' if it is possible to cast an object of class |src| to |
| // an object of class |dst|. |
| bool CanAssignClassVariables(JNIEnv* env, |
| const ScopedJavaLocalRef<jclass>& dst, |
| const ScopedJavaLocalRef<jclass>& src) { |
| if (dst.is_null() || src.is_null()) |
| return false; |
| return env->IsAssignableFrom(src.obj(), dst.obj()) == JNI_TRUE; |
| } |
| |
| ScopedJavaLocalRef<jclass> GetObjectClass( |
| JNIEnv* env, |
| const ScopedJavaLocalRef<jobject>& obj) { |
| jclass clazz = env->GetObjectClass(obj.obj()); |
| return ScopedJavaLocalRef<jclass>(env, clazz); |
| } |
| |
| jvalue CoerceJavaScriptObjectToJavaValue(JNIEnv* env, |
| const base::Value* value, |
| const JavaType& target_type, |
| bool coerce_to_string, |
| const ObjectRefs& object_refs, |
| GinJavaBridgeError* error) { |
| // This covers both JavaScript objects (including arrays) and Java objects. |
| // See http://jdk6.java.net/plugin2/liveconnect/#JS_OTHER_OBJECTS, |
| // http://jdk6.java.net/plugin2/liveconnect/#JS_ARRAY_VALUES and |
| // http://jdk6.java.net/plugin2/liveconnect/#JS_JAVA_OBJECTS |
| jvalue result; |
| switch (target_type.type) { |
| case JavaType::TypeObject: { |
| if (GinJavaBridgeValue::ContainsGinJavaBridgeValue(value)) { |
| scoped_ptr<const GinJavaBridgeValue> gin_value( |
| GinJavaBridgeValue::FromValue(value)); |
| DCHECK(gin_value); |
| DCHECK(gin_value->IsType(GinJavaBridgeValue::TYPE_OBJECT_ID)); |
| ScopedJavaLocalRef<jobject> obj; |
| GinJavaBoundObject::ObjectID object_id; |
| if (gin_value->GetAsObjectID(&object_id)) { |
| ObjectRefs::const_iterator iter = object_refs.find(object_id); |
| if (iter != object_refs.end()) { |
| obj.Reset(iter->second.get(env)); |
| } |
| } |
| DCHECK(!target_type.class_jni_name.empty()); |
| DCHECK(!obj.is_null()); |
| if (CanAssignClassVariables( |
| env, |
| base::android::GetClass(env, target_type.JNIName().c_str()), |
| GetObjectClass(env, obj))) { |
| result.l = obj.Release(); |
| } else { |
| result.l = NULL; |
| *error = kGinJavaBridgeNonAssignableTypes; |
| } |
| } else { |
| // LIVECONNECT_COMPLIANCE: Existing behavior is to pass null. Spec |
| // requires converting if the target type is |
| // netscape.javascript.JSObject, otherwise raising a JavaScript |
| // exception. |
| result.l = NULL; |
| } |
| break; |
| } |
| case JavaType::TypeString: |
| // LIVECONNECT_COMPLIANCE: Existing behavior is to convert to |
| // "undefined". Spec requires calling toString() on the Java object. |
| result.l = |
| coerce_to_string ? ConvertUTF8ToJString(env, kUndefined) : NULL; |
| break; |
| case JavaType::TypeByte: |
| case JavaType::TypeShort: |
| case JavaType::TypeInt: |
| case JavaType::TypeLong: |
| case JavaType::TypeFloat: |
| case JavaType::TypeDouble: |
| case JavaType::TypeChar: { |
| // LIVECONNECT_COMPLIANCE: Existing behavior is to convert to 0. Spec |
| // requires raising a JavaScript exception. |
| jvalue null_value = {0}; |
| result = null_value; |
| break; |
| } |
| case JavaType::TypeBoolean: |
| // LIVECONNECT_COMPLIANCE: Existing behavior is to convert to false. Spec |
| // requires raising a JavaScript exception. |
| result.z = JNI_FALSE; |
| break; |
| case JavaType::TypeArray: |
| if (value->IsType(base::Value::TYPE_DICTIONARY)) { |
| result.l = CoerceJavaScriptDictionaryToArray( |
| env, value, target_type, object_refs, error); |
| } else if (value->IsType(base::Value::TYPE_LIST)) { |
| result.l = CoerceJavaScriptListToArray( |
| env, value, target_type, object_refs, error); |
| } else { |
| result.l = NULL; |
| } |
| break; |
| case JavaType::TypeVoid: |
| // Conversion to void must never happen. |
| NOTREACHED(); |
| break; |
| } |
| return result; |
| } |
| |
| jvalue CoerceGinJavaBridgeValueToJavaValue(JNIEnv* env, |
| const base::Value* value, |
| const JavaType& target_type, |
| bool coerce_to_string, |
| const ObjectRefs& object_refs, |
| GinJavaBridgeError* error) { |
| DCHECK(GinJavaBridgeValue::ContainsGinJavaBridgeValue(value)); |
| scoped_ptr<const GinJavaBridgeValue> gin_value( |
| GinJavaBridgeValue::FromValue(value)); |
| switch (gin_value->GetType()) { |
| case GinJavaBridgeValue::TYPE_UNDEFINED: |
| return CoerceJavaScriptNullOrUndefinedToJavaValue( |
| env, value, target_type, coerce_to_string, error); |
| case GinJavaBridgeValue::TYPE_NONFINITE: { |
| float float_value; |
| gin_value->GetAsNonFinite(&float_value); |
| return CoerceJavaScriptDoubleToJavaValue( |
| env, float_value, target_type, coerce_to_string, error); |
| } |
| case GinJavaBridgeValue::TYPE_OBJECT_ID: |
| return CoerceJavaScriptObjectToJavaValue( |
| env, value, target_type, coerce_to_string, object_refs, error); |
| default: |
| NOTREACHED(); |
| } |
| return jvalue(); |
| } |
| |
| } // namespace |
| |
| |
| void ReleaseJavaValueIfRequired(JNIEnv* env, |
| jvalue* value, |
| const JavaType& type) { |
| if (type.type == JavaType::TypeString || type.type == JavaType::TypeObject || |
| type.type == JavaType::TypeArray) { |
| env->DeleteLocalRef(value->l); |
| value->l = NULL; |
| } |
| } |
| |
| jvalue CoerceJavaScriptValueToJavaValue(JNIEnv* env, |
| const base::Value* value, |
| const JavaType& target_type, |
| bool coerce_to_string, |
| const ObjectRefs& object_refs, |
| GinJavaBridgeError* error) { |
| // Note that in all these conversions, the relevant field of the jvalue must |
| // always be explicitly set, as jvalue does not initialize its fields. |
| |
| switch (value->GetType()) { |
| case base::Value::TYPE_INTEGER: |
| return CoerceJavaScriptIntegerToJavaValue( |
| env, value, target_type, coerce_to_string, error); |
| case base::Value::TYPE_DOUBLE: { |
| double double_value; |
| value->GetAsDouble(&double_value); |
| return CoerceJavaScriptDoubleToJavaValue( |
| env, double_value, target_type, coerce_to_string, error); |
| } |
| case base::Value::TYPE_BOOLEAN: |
| return CoerceJavaScriptBooleanToJavaValue( |
| env, value, target_type, coerce_to_string, error); |
| case base::Value::TYPE_STRING: |
| return CoerceJavaScriptStringToJavaValue(env, value, target_type, error); |
| case base::Value::TYPE_DICTIONARY: |
| case base::Value::TYPE_LIST: |
| return CoerceJavaScriptObjectToJavaValue( |
| env, value, target_type, coerce_to_string, object_refs, error); |
| case base::Value::TYPE_NULL: |
| return CoerceJavaScriptNullOrUndefinedToJavaValue( |
| env, value, target_type, coerce_to_string, error); |
| case base::Value::TYPE_BINARY: |
| return CoerceGinJavaBridgeValueToJavaValue( |
| env, value, target_type, coerce_to_string, object_refs, error); |
| } |
| NOTREACHED(); |
| return jvalue(); |
| } |
| |
| } // namespace content |