Index: content/browser/renderer_host/java/java_bound_object.cc |
diff --git a/content/browser/renderer_host/java/java_bound_object.cc b/content/browser/renderer_host/java/java_bound_object.cc |
deleted file mode 100644 |
index f812c4296838eb5efe5fdeb15e9f2d584aea7953..0000000000000000000000000000000000000000 |
--- a/content/browser/renderer_host/java/java_bound_object.cc |
+++ /dev/null |
@@ -1,1040 +0,0 @@ |
-// 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. |
- |
-#include "content/browser/renderer_host/java/java_bound_object.h" |
- |
-#include "base/android/jni_android.h" |
-#include "base/android/jni_string.h" |
-#include "base/memory/singleton.h" |
-#include "base/numerics/safe_conversions.h" |
-#include "base/strings/string_number_conversions.h" |
-#include "base/strings/stringprintf.h" |
-#include "content/browser/renderer_host/java/java_bridge_dispatcher_host_manager.h" |
-#include "content/browser/renderer_host/java/java_type.h" |
-#include "content/browser/renderer_host/java/jni_helper.h" |
-#include "content/public/browser/browser_thread.h" |
-#include "third_party/WebKit/public/web/WebBindings.h" |
- |
-using base::StringPrintf; |
-using base::android::AttachCurrentThread; |
-using base::android::ConvertUTF8ToJavaString; |
-using base::android::GetClass; |
-using base::android::JavaRef; |
-using base::android::ScopedJavaGlobalRef; |
-using base::android::ScopedJavaLocalRef; |
-using blink::WebBindings; |
- |
-// The conversion between JavaScript and Java types is based on the Live |
-// Connect 2 spec. See |
-// http://jdk6.java.net/plugin2/liveconnect/#JS_JAVA_CONVERSIONS. |
- |
-// Note that in some cases, we differ from from the spec in order to maintain |
-// existing behavior. These areas are marked LIVECONNECT_COMPLIANCE. We may |
-// revisit this decision in the future. |
- |
-namespace content { |
-namespace { |
- |
-const char kJavaLangClass[] = "java/lang/Class"; |
-const char kJavaLangObject[] = "java/lang/Object"; |
-const char kJavaLangReflectMethod[] = "java/lang/reflect/Method"; |
-const char kJavaLangSecurityExceptionClass[] = "java/lang/SecurityException"; |
-const char kGetClass[] = "getClass"; |
-const char kGetMethods[] = "getMethods"; |
-const char kIsAnnotationPresent[] = "isAnnotationPresent"; |
-const char kReturningJavaLangClass[] = "()Ljava/lang/Class;"; |
-const char kReturningJavaLangReflectMethodArray[] = |
- "()[Ljava/lang/reflect/Method;"; |
-const char kTakesJavaLangClassReturningBoolean[] = "(Ljava/lang/Class;)Z"; |
-// This is an exception message, so no need to localize. |
-const char kAccessToObjectGetClassIsBlocked[] = |
- "Access to java.lang.Object.getClass is blocked"; |
- |
-// Our special NPObject type. We extend an NPObject with a pointer to a |
-// JavaBoundObject. We also add static methods for each of the NPObject |
-// callbacks, which are registered by our NPClass. These methods simply |
-// delegate to the private implementation methods of JavaBoundObject. |
-struct JavaNPObject : public NPObject { |
- JavaBoundObject* bound_object; |
- |
- static const NPClass kNPClass; |
- |
- static NPObject* Allocate(NPP npp, NPClass* np_class); |
- static void Deallocate(NPObject* np_object); |
- static bool HasMethod(NPObject* np_object, NPIdentifier np_identifier); |
- static bool Invoke(NPObject* np_object, NPIdentifier np_identifier, |
- const NPVariant *args, uint32_t arg_count, |
- NPVariant *result); |
- static bool HasProperty(NPObject* np_object, NPIdentifier np_identifier); |
- static bool GetProperty(NPObject* np_object, NPIdentifier np_identifier, |
- NPVariant *result); |
- static bool Enumerate(NPObject* object, NPIdentifier** values, |
- uint32_t* count); |
-}; |
- |
-const NPClass JavaNPObject::kNPClass = { |
- NP_CLASS_STRUCT_VERSION, |
- JavaNPObject::Allocate, |
- JavaNPObject::Deallocate, |
- NULL, // NPInvalidate |
- JavaNPObject::HasMethod, |
- JavaNPObject::Invoke, |
- NULL, // NPInvokeDefault |
- JavaNPObject::HasProperty, |
- JavaNPObject::GetProperty, |
- NULL, // NPSetProperty, |
- NULL, // NPRemoveProperty |
- JavaNPObject::Enumerate, |
- NULL, |
-}; |
- |
-NPObject* JavaNPObject::Allocate(NPP npp, NPClass* np_class) { |
- JavaNPObject* obj = new JavaNPObject(); |
- return obj; |
-} |
- |
-void JavaNPObject::Deallocate(NPObject* np_object) { |
- JavaNPObject* obj = reinterpret_cast<JavaNPObject*>(np_object); |
- delete obj->bound_object; |
- delete obj; |
-} |
- |
-bool JavaNPObject::HasMethod(NPObject* np_object, NPIdentifier np_identifier) { |
- std::string name(WebBindings::utf8FromIdentifier(np_identifier)); |
- JavaNPObject* obj = reinterpret_cast<JavaNPObject*>(np_object); |
- return obj->bound_object->HasMethod(name); |
-} |
- |
-bool JavaNPObject::Invoke(NPObject* np_object, NPIdentifier np_identifier, |
- const NPVariant* args, uint32_t arg_count, |
- NPVariant* result) { |
- std::string name(WebBindings::utf8FromIdentifier(np_identifier)); |
- JavaNPObject* obj = reinterpret_cast<JavaNPObject*>(np_object); |
- return obj->bound_object->Invoke(name, args, arg_count, result); |
-} |
- |
-bool JavaNPObject::HasProperty(NPObject* np_object, |
- NPIdentifier np_identifier) { |
- // LIVECONNECT_COMPLIANCE: Existing behavior is to return false to indicate |
- // that the property is not present. Spec requires supporting this correctly. |
- return false; |
-} |
- |
-bool JavaNPObject::GetProperty(NPObject* np_object, |
- NPIdentifier np_identifier, |
- NPVariant* result) { |
- // LIVECONNECT_COMPLIANCE: Existing behavior is to return false to indicate |
- // that the property is undefined. Spec requires supporting this correctly. |
- return false; |
-} |
- |
-bool JavaNPObject::Enumerate(NPObject* np_object, NPIdentifier** values, |
- uint32_t* count) { |
- JavaNPObject* obj = reinterpret_cast<JavaNPObject*>(np_object); |
- if (!obj->bound_object->CanEnumerateMethods()) return false; |
- std::vector<std::string> method_names = obj->bound_object->GetMethodNames(); |
- *count = base::saturated_cast<uint32_t>(method_names.size()); |
- *values = static_cast<NPIdentifier*>(calloc(*count, sizeof(NPIdentifier))); |
- for (uint32_t i = 0; i < *count; ++i) { |
- (*values)[i] = WebBindings::getStringIdentifier(method_names[i].c_str()); |
- } |
- return true; |
-} |
- |
-// Calls a Java method through JNI. If the Java method raises an uncaught |
-// exception, it is cleared and this method returns false. Otherwise, this |
-// method returns true and the Java method's return value is provided as an |
-// NPVariant. Note that this method does not do any type coercion. The Java |
-// return value is simply converted to the corresponding NPAPI type. |
-bool CallJNIMethod( |
- jobject object, |
- jclass clazz, |
- const JavaType& return_type, |
- jmethodID id, |
- jvalue* parameters, |
- NPVariant* result, |
- const JavaRef<jclass>& safe_annotation_clazz, |
- const base::WeakPtr<JavaBridgeDispatcherHostManager>& manager, |
- bool can_enumerate_methods) { |
- DCHECK(object || clazz); |
- JNIEnv* env = AttachCurrentThread(); |
- switch (return_type.type) { |
- case JavaType::TypeBoolean: |
- BOOLEAN_TO_NPVARIANT( |
- object ? env->CallBooleanMethodA(object, id, parameters) |
- : env->CallStaticBooleanMethodA(clazz, id, parameters), |
- *result); |
- break; |
- case JavaType::TypeByte: |
- INT32_TO_NPVARIANT( |
- object ? env->CallByteMethodA(object, id, parameters) |
- : env->CallStaticByteMethodA(clazz, id, parameters), |
- *result); |
- break; |
- case JavaType::TypeChar: |
- INT32_TO_NPVARIANT( |
- object ? env->CallCharMethodA(object, id, parameters) |
- : env->CallStaticCharMethodA(clazz, id, parameters), |
- *result); |
- break; |
- case JavaType::TypeShort: |
- INT32_TO_NPVARIANT( |
- object ? env->CallShortMethodA(object, id, parameters) |
- : env->CallStaticShortMethodA(clazz, id, parameters), |
- *result); |
- break; |
- case JavaType::TypeInt: |
- INT32_TO_NPVARIANT(object |
- ? env->CallIntMethodA(object, id, parameters) |
- : env->CallStaticIntMethodA(clazz, id, parameters), |
- *result); |
- break; |
- case JavaType::TypeLong: |
- DOUBLE_TO_NPVARIANT( |
- object ? env->CallLongMethodA(object, id, parameters) |
- : env->CallStaticLongMethodA(clazz, id, parameters), |
- *result); |
- break; |
- case JavaType::TypeFloat: |
- DOUBLE_TO_NPVARIANT( |
- object ? env->CallFloatMethodA(object, id, parameters) |
- : env->CallStaticFloatMethodA(clazz, id, parameters), |
- *result); |
- break; |
- case JavaType::TypeDouble: |
- DOUBLE_TO_NPVARIANT( |
- object ? env->CallDoubleMethodA(object, id, parameters) |
- : env->CallStaticDoubleMethodA(clazz, id, parameters), |
- *result); |
- break; |
- case JavaType::TypeVoid: |
- if (object) |
- env->CallVoidMethodA(object, id, parameters); |
- else |
- env->CallStaticVoidMethodA(clazz, id, parameters); |
- VOID_TO_NPVARIANT(*result); |
- break; |
- case JavaType::TypeArray: |
- // LIVECONNECT_COMPLIANCE: Existing behavior is to not call methods that |
- // return arrays. Spec requires calling the method and converting the |
- // result to a JavaScript array. |
- VOID_TO_NPVARIANT(*result); |
- break; |
- case JavaType::TypeString: { |
- jstring java_string = static_cast<jstring>( |
- object ? env->CallObjectMethodA(object, id, parameters) |
- : env->CallStaticObjectMethodA(clazz, id, parameters)); |
- // If an exception was raised, we must clear it before calling most JNI |
- // methods. ScopedJavaLocalRef is liable to make such calls, so we test |
- // first. |
- if (base::android::ClearException(env)) { |
- return false; |
- } |
- ScopedJavaLocalRef<jstring> scoped_java_string(env, java_string); |
- if (!scoped_java_string.obj()) { |
- // LIVECONNECT_COMPLIANCE: Existing behavior is to return undefined. |
- // Spec requires returning a null string. |
- VOID_TO_NPVARIANT(*result); |
- break; |
- } |
- std::string str = |
- base::android::ConvertJavaStringToUTF8(scoped_java_string); |
- size_t length = str.length(); |
- // This pointer is freed in _NPN_ReleaseVariantValue in |
- // third_party/WebKit/Source/WebCore/bindings/v8/npruntime.cpp. |
- char* buffer = static_cast<char*>(malloc(length)); |
- str.copy(buffer, length, 0); |
- STRINGN_TO_NPVARIANT(buffer, length, *result); |
- break; |
- } |
- case JavaType::TypeObject: { |
- // If an exception was raised, we must clear it before calling most JNI |
- // methods. ScopedJavaLocalRef is liable to make such calls, so we test |
- // first. |
- jobject java_object = |
- object ? env->CallObjectMethodA(object, id, parameters) |
- : env->CallStaticObjectMethodA(clazz, id, parameters); |
- if (base::android::ClearException(env)) { |
- return false; |
- } |
- ScopedJavaLocalRef<jobject> scoped_java_object(env, java_object); |
- if (!scoped_java_object.obj()) { |
- NULL_TO_NPVARIANT(*result); |
- break; |
- } |
- OBJECT_TO_NPVARIANT(JavaBoundObject::Create(scoped_java_object, |
- safe_annotation_clazz, |
- manager, |
- can_enumerate_methods), |
- *result); |
- break; |
- } |
- } |
- return !base::android::ClearException(env); |
-} |
- |
-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 limit = (GG_INT64_C(1) << 63) - static_cast<uint64>(1 << 10); |
- DCHECK(limit > 0); |
- const double kLargestDoubleLessThanInt64Max = limit; |
- const double kSmallestDoubleGreaterThanInt64Min = -limit; |
- 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 CoerceJavaScriptNumberToJavaValue(const NPVariant& variant, |
- const JavaType& target_type, |
- bool coerce_to_string) { |
- // 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. For double values, the logic is more involved. |
- jvalue result; |
- DCHECK(variant.type == NPVariantType_Int32 || |
- variant.type == NPVariantType_Double); |
- bool is_double = variant.type == NPVariantType_Double; |
- switch (target_type.type) { |
- case JavaType::TypeByte: |
- result.b = is_double ? |
- static_cast<jbyte>(RoundDoubleToInt(NPVARIANT_TO_DOUBLE(variant))) : |
- static_cast<jbyte>(NPVARIANT_TO_INT32(variant)); |
- 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 = is_double ? 0 : |
- static_cast<jchar>(NPVARIANT_TO_INT32(variant)); |
- break; |
- case JavaType::TypeShort: |
- result.s = is_double ? |
- static_cast<jshort>(RoundDoubleToInt(NPVARIANT_TO_DOUBLE(variant))) : |
- static_cast<jshort>(NPVARIANT_TO_INT32(variant)); |
- break; |
- case JavaType::TypeInt: |
- result.i = is_double ? RoundDoubleToInt(NPVARIANT_TO_DOUBLE(variant)) : |
- NPVARIANT_TO_INT32(variant); |
- break; |
- case JavaType::TypeLong: |
- result.j = is_double ? RoundDoubleToLong(NPVARIANT_TO_DOUBLE(variant)) : |
- NPVARIANT_TO_INT32(variant); |
- break; |
- case JavaType::TypeFloat: |
- result.f = is_double ? static_cast<jfloat>(NPVARIANT_TO_DOUBLE(variant)) : |
- NPVARIANT_TO_INT32(variant); |
- break; |
- case JavaType::TypeDouble: |
- result.d = is_double ? NPVARIANT_TO_DOUBLE(variant) : |
- NPVARIANT_TO_INT32(variant); |
- 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 ? |
- ConvertUTF8ToJavaString( |
- AttachCurrentThread(), |
- is_double ? |
- base::StringPrintf("%.6lg", NPVARIANT_TO_DOUBLE(variant)) : |
- base::Int64ToString(NPVARIANT_TO_INT32(variant))).Release() : |
- 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(const NPVariant& variant, |
- 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); |
- 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 ? |
- ConvertUTF8ToJavaString(AttachCurrentThread(), |
- boolean_value ? "true" : "false").Release() : |
- 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(const NPVariant& variant, |
- const JavaType& target_type) { |
- // See http://jdk6.java.net/plugin2/liveconnect/#JS_STRING_VALUES. |
- DCHECK_EQ(NPVariantType_String, variant.type); |
- jvalue result; |
- switch (target_type.type) { |
- case JavaType::TypeString: |
- result.l = ConvertUTF8ToJavaString( |
- AttachCurrentThread(), |
- base::StringPiece(NPVARIANT_TO_STRING(variant).UTF8Characters, |
- NPVARIANT_TO_STRING(variant).UTF8Length)).Release(); |
- 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(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(GetClass(env, "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; |
-} |
- |
-// 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(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); |
-} |
- |
-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(const NPVariant& variant, |
- const JavaType& target_type, |
- bool coerce_to_string); |
- |
-// Returns a new local reference to a Java array. |
-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. |
- // TODO(steveblock): Handle failure to create the array. |
- jobject result = CreateJavaArray(target_inner_type, length); |
- NPVariant value_variant; |
- JNIEnv* env = AttachCurrentThread(); |
- 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); |
- jvalue element = CoerceJavaScriptValueToJavaValue(value_variant, |
- target_inner_type, |
- false); |
- SetArrayElement(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); |
- WebBindings::releaseVariantValue(&value_variant); |
- } |
- |
- return result; |
-} |
- |
-jvalue CoerceJavaScriptObjectToJavaValue(const NPVariant& variant, |
- 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 |
- // http://jdk6.java.net/plugin2/liveconnect/#JS_JAVA_OBJECTS |
- DCHECK_EQ(NPVariantType_Object, variant.type); |
- |
- NPObject* object = NPVARIANT_TO_OBJECT(variant); |
- bool is_java_object = &JavaNPObject::kNPClass == object->_class; |
- |
- jvalue result; |
- switch (target_type.type) { |
- case JavaType::TypeObject: |
- if (is_java_object) { |
- // LIVECONNECT_COMPLIANCE: Existing behavior is to pass all Java |
- // objects. Spec requires passing only Java objects which are |
- // assignment-compatibile. |
- result.l = AttachCurrentThread()->NewLocalRef( |
- JavaBoundObject::GetJavaObject(object).obj()); |
- } 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 ? |
- ConvertUTF8ToJavaString(AttachCurrentThread(), "undefined"). |
- Release() : |
- 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 (is_java_object) { |
- // LIVECONNECT_COMPLIANCE: Existing behavior is to convert to NULL. Spec |
- // requires raising a JavaScript exception. |
- result.l = NULL; |
- } else { |
- result.l = CoerceJavaScriptObjectToArray(variant, target_type); |
- } |
- break; |
- case JavaType::TypeVoid: |
- // Conversion to void must never happen. |
- NOTREACHED(); |
- break; |
- } |
- return result; |
-} |
- |
-jvalue CoerceJavaScriptNullOrUndefinedToJavaValue(const NPVariant& variant, |
- 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); |
- 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 && variant.type == NPVariantType_Void) ? |
- ConvertUTF8ToJavaString(AttachCurrentThread(), "undefined"). |
- Release() : |
- 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; |
-} |
- |
-// 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. |
-// |
-// Note that the jvalue returned by this method may contain a new local |
-// reference to an object (string, object or array). This must be released by |
-// the caller. |
-jvalue CoerceJavaScriptValueToJavaValue(const NPVariant& variant, |
- 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. |
- |
- switch (variant.type) { |
- case NPVariantType_Int32: |
- case NPVariantType_Double: |
- return CoerceJavaScriptNumberToJavaValue(variant, target_type, |
- coerce_to_string); |
- case NPVariantType_Bool: |
- return CoerceJavaScriptBooleanToJavaValue(variant, target_type, |
- coerce_to_string); |
- case NPVariantType_String: |
- return CoerceJavaScriptStringToJavaValue(variant, target_type); |
- case NPVariantType_Object: |
- return CoerceJavaScriptObjectToJavaValue(variant, target_type, |
- coerce_to_string); |
- case NPVariantType_Null: |
- case NPVariantType_Void: |
- return CoerceJavaScriptNullOrUndefinedToJavaValue(variant, target_type, |
- coerce_to_string); |
- } |
- NOTREACHED(); |
- return jvalue(); |
-} |
- |
-} // namespace |
- |
-NPObject* JavaBoundObject::Create( |
- const JavaRef<jobject>& object, |
- const JavaRef<jclass>& safe_annotation_clazz, |
- const base::WeakPtr<JavaBridgeDispatcherHostManager>& manager, |
- bool can_enumerate_methods) { |
- // The first argument (a plugin's instance handle) is passed through to the |
- // allocate function directly, and we don't use it, so it's ok to be 0. |
- // The object is created with a ref count of one. |
- NPObject* np_object = WebBindings::createObject(0, const_cast<NPClass*>( |
- &JavaNPObject::kNPClass)); |
- // The NPObject takes ownership of the JavaBoundObject. |
- reinterpret_cast<JavaNPObject*>(np_object)->bound_object = |
- new JavaBoundObject( |
- object, safe_annotation_clazz, manager, can_enumerate_methods); |
- return np_object; |
-} |
- |
-JavaBoundObject::JavaBoundObject( |
- const JavaRef<jobject>& object, |
- const JavaRef<jclass>& safe_annotation_clazz, |
- const base::WeakPtr<JavaBridgeDispatcherHostManager>& manager, |
- bool can_enumerate_methods) |
- : java_object_(AttachCurrentThread(), object.obj()), |
- manager_(manager), |
- are_methods_set_up_(false), |
- object_get_class_method_id_(NULL), |
- can_enumerate_methods_(can_enumerate_methods), |
- safe_annotation_clazz_(safe_annotation_clazz) { |
- BrowserThread::PostTask( |
- BrowserThread::UI, FROM_HERE, |
- base::Bind(&JavaBridgeDispatcherHostManager::JavaBoundObjectCreated, |
- manager_, |
- base::android::ScopedJavaGlobalRef<jobject>(object))); |
- // Other than informing the JavaBridgeDispatcherHostManager that a java bound |
- // object has been created (above), we don't do anything else with our Java |
- // object when first created. We do it all lazily when a method is first |
- // invoked. |
-} |
- |
-JavaBoundObject::~JavaBoundObject() { |
- BrowserThread::PostTask( |
- BrowserThread::UI, FROM_HERE, |
- base::Bind(&JavaBridgeDispatcherHostManager::JavaBoundObjectDestroyed, |
- manager_, |
- base::android::ScopedJavaGlobalRef<jobject>( |
- java_object_.get(AttachCurrentThread())))); |
-} |
- |
-ScopedJavaLocalRef<jobject> JavaBoundObject::GetJavaObject(NPObject* object) { |
- DCHECK_EQ(&JavaNPObject::kNPClass, object->_class); |
- JavaBoundObject* jbo = reinterpret_cast<JavaNPObject*>(object)->bound_object; |
- return jbo->java_object_.get(AttachCurrentThread()); |
-} |
- |
-std::vector<std::string> JavaBoundObject::GetMethodNames() const { |
- EnsureMethodsAreSetUp(); |
- std::vector<std::string> result; |
- for (JavaMethodMap::const_iterator it = methods_.begin(); |
- it != methods_.end(); |
- it = methods_.upper_bound(it->first)) { |
- result.push_back(it->first); |
- } |
- return result; |
-} |
- |
-bool JavaBoundObject::HasMethod(const std::string& name) const { |
- EnsureMethodsAreSetUp(); |
- return methods_.find(name) != methods_.end(); |
-} |
- |
-bool JavaBoundObject::Invoke(const std::string& name, const NPVariant* args, |
- size_t arg_count, NPVariant* result) { |
- EnsureMethodsAreSetUp(); |
- |
- // Get all methods with the correct name. |
- std::pair<JavaMethodMap::const_iterator, JavaMethodMap::const_iterator> |
- iters = methods_.equal_range(name); |
- if (iters.first == iters.second) { |
- return false; |
- } |
- |
- // Take the first method with the correct number of arguments. |
- JavaMethod* method = NULL; |
- for (JavaMethodMap::const_iterator iter = iters.first; iter != iters.second; |
- ++iter) { |
- if (iter->second->num_parameters() == arg_count) { |
- method = iter->second.get(); |
- break; |
- } |
- } |
- if (!method) { |
- return false; |
- } |
- |
- // Block access to java.lang.Object.getClass. |
- // As it is declared to be final, it is sufficient to compare methodIDs. |
- if (method->id() == object_get_class_method_id_) { |
- BrowserThread::PostTask( |
- BrowserThread::UI, FROM_HERE, |
- base::Bind(&JavaBoundObject::ThrowSecurityException, |
- kAccessToObjectGetClassIsBlocked)); |
- return false; |
- } |
- |
- // Coerce |
- std::vector<jvalue> parameters(arg_count); |
- for (size_t i = 0; i < arg_count; ++i) { |
- parameters[i] = CoerceJavaScriptValueToJavaValue(args[i], |
- method->parameter_type(i), |
- true); |
- } |
- |
- JNIEnv* env = AttachCurrentThread(); |
- |
- ScopedJavaLocalRef<jobject> obj; |
- ScopedJavaLocalRef<jclass> cls; |
- bool ok = false; |
- if (method->is_static()) { |
- cls = GetLocalClassRef(env); |
- } else { |
- obj = java_object_.get(env); |
- } |
- if (!obj.is_null() || !cls.is_null()) { |
- // Call |
- ok = CallJNIMethod(obj.obj(), cls.obj(), method->return_type(), |
- method->id(), ¶meters[0], result, |
- safe_annotation_clazz_, |
- manager_, |
- can_enumerate_methods_); |
- } |
- |
- // Now that we're done with the jvalue, release any local references created |
- // by CoerceJavaScriptValueToJavaValue(). |
- for (size_t i = 0; i < arg_count; ++i) { |
- ReleaseJavaValueIfRequired(env, ¶meters[i], method->parameter_type(i)); |
- } |
- |
- return ok; |
-} |
- |
-ScopedJavaLocalRef<jclass> JavaBoundObject::GetLocalClassRef( |
- JNIEnv* env) const { |
- if (!object_get_class_method_id_) { |
- object_get_class_method_id_ = GetMethodIDFromClassName( |
- env, kJavaLangObject, kGetClass, kReturningJavaLangClass); |
- } |
- |
- ScopedJavaLocalRef<jobject> obj = java_object_.get(env); |
- if (!obj.is_null()) { |
- return ScopedJavaLocalRef<jclass>(env, static_cast<jclass>( |
- env->CallObjectMethod(obj.obj(), object_get_class_method_id_))); |
- } else { |
- return ScopedJavaLocalRef<jclass>(); |
- } |
-} |
- |
-void JavaBoundObject::EnsureMethodsAreSetUp() const { |
- if (are_methods_set_up_) |
- return; |
- are_methods_set_up_ = true; |
- |
- JNIEnv* env = AttachCurrentThread(); |
- |
- ScopedJavaLocalRef<jclass> clazz = GetLocalClassRef(env); |
- if (clazz.is_null()) { |
- return; |
- } |
- |
- ScopedJavaLocalRef<jobjectArray> methods(env, static_cast<jobjectArray>( |
- env->CallObjectMethod(clazz.obj(), GetMethodIDFromClassName( |
- env, |
- kJavaLangClass, |
- kGetMethods, |
- kReturningJavaLangReflectMethodArray)))); |
- |
- size_t num_methods = env->GetArrayLength(methods.obj()); |
- // Java objects always have public methods. |
- DCHECK(num_methods); |
- |
- for (size_t i = 0; i < num_methods; ++i) { |
- ScopedJavaLocalRef<jobject> java_method( |
- env, |
- env->GetObjectArrayElement(methods.obj(), i)); |
- |
- if (!safe_annotation_clazz_.is_null()) { |
- jboolean safe = env->CallBooleanMethod(java_method.obj(), |
- GetMethodIDFromClassName( |
- env, |
- kJavaLangReflectMethod, |
- kIsAnnotationPresent, |
- kTakesJavaLangClassReturningBoolean), |
- safe_annotation_clazz_.obj()); |
- |
- if (!safe) |
- continue; |
- } |
- |
- JavaMethod* method = new JavaMethod(java_method); |
- methods_.insert(std::make_pair(method->name(), method)); |
- } |
-} |
- |
-// static |
-void JavaBoundObject::ThrowSecurityException(const char* message) { |
- DCHECK_CURRENTLY_ON(BrowserThread::UI); |
- JNIEnv* env = AttachCurrentThread(); |
- base::android::ScopedJavaLocalRef<jclass> clazz( |
- env, env->FindClass(kJavaLangSecurityExceptionClass)); |
- env->ThrowNew(clazz.obj(), message); |
-} |
- |
-} // namespace content |