| 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
|
|
|