diff options
author | steveblock@chromium.org <steveblock@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2012-01-08 17:35:05 +0000 |
---|---|---|
committer | steveblock@chromium.org <steveblock@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2012-01-08 17:35:05 +0000 |
commit | aeb3abde5a3bc3a411ff5ee098aab8ae0378d4c5 (patch) | |
tree | c9567b53610fd5152edb09fb26e8c497900aad79 /content/browser/renderer_host/java/java_bound_object.cc | |
parent | c7582dddf3484102fc3e51450e888019b519acd8 (diff) | |
download | chromium_src-aeb3abde5a3bc3a411ff5ee098aab8ae0378d4c5.zip chromium_src-aeb3abde5a3bc3a411ff5ee098aab8ae0378d4c5.tar.gz chromium_src-aeb3abde5a3bc3a411ff5ee098aab8ae0378d4c5.tar.bz2 |
Fix the Java Bridge to support coercing to Java arrays
This change adds support for coercing to Java arrays when passing JavaScript
objects to the methods of Java objects injected using the Java Bridge.
- Modify JavaType to store the type of the array elements when the type of the
value is TypeArray.
- Add logic to create Java arrays and to interrogate JavaScript objects for
their length property and array elements.
- Modify existing CoerceJavaScriptXXXToJavaValue() methods to take an
additional coerce_to_string argument. This is required because we require
slightly different behaviour when coercing to Java strings, depending on
whether or not the value is an array element.
- Modify JavaBridgeChannel to override
NPChannelBase::OnControlMessageReceived() to avoid DCHECK().
Also fix a potential overflow when converting JavaScript numbers to Java
strings.
BUG=105547
Review URL: http://codereview.chromium.org/9006025
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@116848 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'content/browser/renderer_host/java/java_bound_object.cc')
-rw-r--r-- | content/browser/renderer_host/java/java_bound_object.cc | 231 |
1 files changed, 203 insertions, 28 deletions
diff --git a/content/browser/renderer_host/java/java_bound_object.cc b/content/browser/renderer_host/java/java_bound_object.cc index 1fd4a74..ced5106 100644 --- a/content/browser/renderer_host/java/java_bound_object.cc +++ b/content/browser/renderer_host/java/java_bound_object.cc @@ -1,4 +1,4 @@ -// Copyright (c) 2011 The Chromium Authors. All rights reserved. +// Copyright (c) 2012 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. @@ -193,7 +193,8 @@ NPVariant CallJNIMethod(jobject object, const JavaType& return_type, } jvalue CoerceJavaScriptNumberToJavaValue(const NPVariant& variant, - const JavaType& target_type) { + const JavaType& target_type, + bool coerce_to_string) { // See http://jdk6.java.net/plugin2/liveconnect/#JS_NUMBER_VALUES. jvalue result; DCHECK(variant.type == NPVariantType_Int32 || @@ -236,10 +237,12 @@ jvalue CoerceJavaScriptNumberToJavaValue(const NPVariant& variant, result.l = NULL; break; case JavaType::TypeString: - result.l = ConvertUTF8ToJavaString( - AttachCurrentThread(), - is_double ? StringPrintf("%.6lg", NPVARIANT_TO_DOUBLE(variant)) : - base::IntToString(NPVARIANT_TO_INT32(variant))); + result.l = coerce_to_string ? + ConvertUTF8ToJavaString( + AttachCurrentThread(), + is_double ? StringPrintf("%.6lg", NPVARIANT_TO_DOUBLE(variant)) : + base::Int64ToString(NPVARIANT_TO_INT32(variant))) : + NULL; break; case JavaType::TypeBoolean: // LIVECONNECT_COMPLIANCE: Existing behavior is to convert to false. Spec @@ -260,7 +263,8 @@ jvalue CoerceJavaScriptNumberToJavaValue(const NPVariant& variant, } jvalue CoerceJavaScriptBooleanToJavaValue(const NPVariant& variant, - const JavaType& target_type) { + const JavaType& target_type, + bool coerce_to_string) { // See http://jdk6.java.net/plugin2/liveconnect/#JS_BOOLEAN_VALUES. DCHECK_EQ(NPVariantType_Bool, variant.type); bool boolean_value = NPVARIANT_TO_BOOLEAN(variant); @@ -275,8 +279,10 @@ jvalue CoerceJavaScriptBooleanToJavaValue(const NPVariant& variant, result.l = NULL; break; case JavaType::TypeString: - result.l = ConvertUTF8ToJavaString(AttachCurrentThread(), - boolean_value ? "true" : "false"); + result.l = coerce_to_string ? + ConvertUTF8ToJavaString(AttachCurrentThread(), + boolean_value ? "true" : "false") : + NULL; break; case JavaType::TypeByte: case JavaType::TypeChar: @@ -356,8 +362,168 @@ jvalue CoerceJavaScriptStringToJavaValue(const NPVariant& variant, return result; } +// Note that this only handles primitive types and strings. +jobject CreateJavaArray(const JavaType& type, jsize length) { + JNIEnv* env = AttachCurrentThread(); + 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(env, env->FindClass("java/lang/String")); + 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; +} + +// Note that this only handles primitive types and strings. +void SetArrayElement(jobject array, + const JavaType& type, + jsize index, + const jvalue& value) { + JNIEnv* env = AttachCurrentThread(); + 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 CoerceJavaScriptValueToJavaValue(const NPVariant& variant, + const JavaType& target_type, + bool coerce_to_string); + +jobject CoerceJavaScriptObjectToArray(const NPVariant& variant, + const JavaType& target_type) { + DCHECK_EQ(JavaType::TypeArray, target_type.type); + NPObject* object = NPVARIANT_TO_OBJECT(variant); + DCHECK_NE(&JavaNPObject::kNPClass, object->_class); + + 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; + } + + // If the object does not have a length property, return null. + NPVariant length_variant; + if (!WebBindings::getProperty(0, object, + WebBindings::getStringIdentifier("length"), + &length_variant)) { + WebBindings::releaseVariantValue(&length_variant); + 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 (NPVARIANT_IS_INT32(length_variant) + && NPVARIANT_TO_INT32(length_variant) >= 0) { + length = NPVARIANT_TO_INT32(length_variant); + } else if (NPVARIANT_IS_DOUBLE(length_variant) + && NPVARIANT_TO_DOUBLE(length_variant) >= 0.0 + && NPVARIANT_TO_DOUBLE(length_variant) <= kint32max) { + length = static_cast<jsize>(NPVARIANT_TO_DOUBLE(length_variant)); + } + WebBindings::releaseVariantValue(&length_variant); + if (length == -1) { + return NULL; + } + + // Create the Java array. Note that we don't explicitly release the local + // ref to the result or any of its elements. + // TODO(steveblock): Handle failure to create the array. + jobject result = CreateJavaArray(target_inner_type, length); + NPVariant value_variant; + for (jsize i = 0; i < length; ++i) { + // It seems that getProperty() will set the variant to type void on failure, + // but this doesn't seem to be documented, so do it explicitly here for + // safety. + VOID_TO_NPVARIANT(value_variant); + // If this fails, for example due to a missing element, we simply treat the + // value as JavaScript undefined. + WebBindings::getProperty(0, object, WebBindings::getIntIdentifier(i), + &value_variant); + SetArrayElement(result, target_inner_type, i, + CoerceJavaScriptValueToJavaValue(value_variant, + target_inner_type, + false)); + WebBindings::releaseVariantValue(&value_variant); + } + + return result; +} + jvalue CoerceJavaScriptObjectToJavaValue(const NPVariant& variant, - const JavaType& target_type) { + const JavaType& target_type, + bool coerce_to_string) { // 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 @@ -387,7 +553,9 @@ jvalue CoerceJavaScriptObjectToJavaValue(const NPVariant& variant, case JavaType::TypeString: // LIVECONNECT_COMPLIANCE: Existing behavior is to convert to // "undefined". Spec requires calling toString() on the Java object. - result.l = ConvertUTF8ToJavaString(AttachCurrentThread(), "undefined"); + result.l = coerce_to_string ? + ConvertUTF8ToJavaString(AttachCurrentThread(), "undefined") : + NULL; break; case JavaType::TypeByte: case JavaType::TypeShort: @@ -413,9 +581,7 @@ jvalue CoerceJavaScriptObjectToJavaValue(const NPVariant& variant, // requires raising a JavaScript exception. result.l = NULL; } else { - // TODO(steveblock): Handle converting JavaScript objects to Java - // arrays. - result.l = NULL; + result.l = CoerceJavaScriptObjectToArray(variant, target_type); } break; case JavaType::TypeVoid: @@ -427,7 +593,8 @@ jvalue CoerceJavaScriptObjectToJavaValue(const NPVariant& variant, } jvalue CoerceJavaScriptNullOrUndefinedToJavaValue(const NPVariant& variant, - const JavaType& target_type) { + const JavaType& target_type, + bool coerce_to_string) { // See http://jdk6.java.net/plugin2/liveconnect/#JS_NULL. DCHECK(variant.type == NPVariantType_Null || variant.type == NPVariantType_Void); @@ -437,13 +604,11 @@ jvalue CoerceJavaScriptNullOrUndefinedToJavaValue(const NPVariant& variant, result.l = NULL; break; case JavaType::TypeString: - if (variant.type == NPVariantType_Void) { - // LIVECONNECT_COMPLIANCE: Existing behavior is to convert undefined to - // "undefined". Spec requires converting undefined to NULL. - result.l = ConvertUTF8ToJavaString(AttachCurrentThread(), "undefined"); - } else { - result.l = NULL; - } + // LIVECONNECT_COMPLIANCE: Existing behavior is to convert undefined to + // "undefined". Spec requires converting undefined to NULL. + result.l = (coerce_to_string && variant.type == NPVariantType_Void) ? + ConvertUTF8ToJavaString(AttachCurrentThread(), "undefined") : + NULL; break; case JavaType::TypeByte: case JavaType::TypeChar: @@ -472,8 +637,13 @@ jvalue CoerceJavaScriptNullOrUndefinedToJavaValue(const NPVariant& variant, return result; } +// coerce_to_string means that we should try to coerce all JavaScript values to +// strings when required, rather than simply converting to NULL. This is used +// to maintain current behaviour, which differs slightly depending upon whether +// or not the coercion in question is for an array element. jvalue CoerceJavaScriptValueToJavaValue(const NPVariant& variant, - const JavaType& target_type) { + const JavaType& target_type, + bool coerce_to_string) { // Note that in all these conversions, the relevant field of the jvalue must // always be explicitly set, as jvalue does not initialize its fields. @@ -483,16 +653,20 @@ jvalue CoerceJavaScriptValueToJavaValue(const NPVariant& variant, switch (variant.type) { case NPVariantType_Int32: case NPVariantType_Double: - return CoerceJavaScriptNumberToJavaValue(variant, target_type); + return CoerceJavaScriptNumberToJavaValue(variant, target_type, + coerce_to_string); case NPVariantType_Bool: - return CoerceJavaScriptBooleanToJavaValue(variant, target_type); + return CoerceJavaScriptBooleanToJavaValue(variant, target_type, + coerce_to_string); case NPVariantType_String: return CoerceJavaScriptStringToJavaValue(variant, target_type); case NPVariantType_Object: - return CoerceJavaScriptObjectToJavaValue(variant, target_type); + return CoerceJavaScriptObjectToJavaValue(variant, target_type, + coerce_to_string); case NPVariantType_Null: case NPVariantType_Void: - return CoerceJavaScriptNullOrUndefinedToJavaValue(variant, target_type); + return CoerceJavaScriptNullOrUndefinedToJavaValue(variant, target_type, + coerce_to_string); } NOTREACHED(); return jvalue(); @@ -564,7 +738,8 @@ bool JavaBoundObject::Invoke(const std::string& name, const NPVariant* args, std::vector<jvalue> parameters(arg_count); for (size_t i = 0; i < arg_count; ++i) { parameters[i] = CoerceJavaScriptValueToJavaValue(args[i], - method->parameter_type(i)); + method->parameter_type(i), + true); } // Call |