summaryrefslogtreecommitdiff
path: root/chromium/content/browser/renderer_host/java
diff options
context:
space:
mode:
authorZeno Albisser <zeno.albisser@digia.com>2013-08-15 21:46:11 +0200
committerZeno Albisser <zeno.albisser@digia.com>2013-08-15 21:46:11 +0200
commit679147eead574d186ebf3069647b4c23e8ccace6 (patch)
treefc247a0ac8ff119f7c8550879ebb6d3dd8d1ff69 /chromium/content/browser/renderer_host/java
downloadqtwebengine-chromium-679147eead574d186ebf3069647b4c23e8ccace6.tar.gz
Initial import.
Diffstat (limited to 'chromium/content/browser/renderer_host/java')
-rw-r--r--chromium/content/browser/renderer_host/java/DEPS4
-rw-r--r--chromium/content/browser/renderer_host/java/OWNERS2
-rw-r--r--chromium/content/browser/renderer_host/java/java_bound_object.cc938
-rw-r--r--chromium/content/browser/renderer_host/java/java_bound_object.h89
-rw-r--r--chromium/content/browser/renderer_host/java/java_bridge_channel_host.cc95
-rw-r--r--chromium/content/browser/renderer_host/java/java_bridge_channel_host.h52
-rw-r--r--chromium/content/browser/renderer_host/java/java_bridge_dispatcher_host.cc173
-rw-r--r--chromium/content/browser/renderer_host/java/java_bridge_dispatcher_host.h75
-rw-r--r--chromium/content/browser/renderer_host/java/java_bridge_dispatcher_host_manager.cc156
-rw-r--r--chromium/content/browser/renderer_host/java/java_bridge_dispatcher_host_manager.h70
-rw-r--r--chromium/content/browser/renderer_host/java/java_method.cc237
-rw-r--r--chromium/content/browser/renderer_host/java/java_method.h47
-rw-r--r--chromium/content/browser/renderer_host/java/java_type.cc114
-rw-r--r--chromium/content/browser/renderer_host/java/java_type.h50
14 files changed, 2102 insertions, 0 deletions
diff --git a/chromium/content/browser/renderer_host/java/DEPS b/chromium/content/browser/renderer_host/java/DEPS
new file mode 100644
index 00000000000..05512435883
--- /dev/null
+++ b/chromium/content/browser/renderer_host/java/DEPS
@@ -0,0 +1,4 @@
+include_rules = [
+ "+content/child", # For java bridge bindings
+ "+third_party/WebKit/public/web/WebBindings.h", # For java bridge bindings
+]
diff --git a/chromium/content/browser/renderer_host/java/OWNERS b/chromium/content/browser/renderer_host/java/OWNERS
new file mode 100644
index 00000000000..4b297c43ee6
--- /dev/null
+++ b/chromium/content/browser/renderer_host/java/OWNERS
@@ -0,0 +1,2 @@
+joth@chromium.org
+steveblock@chromium.org
diff --git a/chromium/content/browser/renderer_host/java/java_bound_object.cc b/chromium/content/browser/renderer_host/java/java_bound_object.cc
new file mode 100644
index 00000000000..4199cca77b8
--- /dev/null
+++ b/chromium/content/browser/renderer_host/java/java_bound_object.cc
@@ -0,0 +1,938 @@
+// 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/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/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::GetMethodIDFromClassName;
+using base::android::JavaRef;
+using base::android::ScopedJavaGlobalRef;
+using base::android::ScopedJavaLocalRef;
+using WebKit::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 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";
+
+// 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);
+};
+
+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
+};
+
+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;
+}
+
+// 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,
+ const JavaType& return_type,
+ jmethodID id,
+ jvalue* parameters,
+ NPVariant* result,
+ const JavaRef<jclass>& safe_annotation_clazz,
+ const base::WeakPtr<JavaBridgeDispatcherHostManager>& manager) {
+ JNIEnv* env = AttachCurrentThread();
+ switch (return_type.type) {
+ case JavaType::TypeBoolean:
+ BOOLEAN_TO_NPVARIANT(env->CallBooleanMethodA(object, id, parameters),
+ *result);
+ break;
+ case JavaType::TypeByte:
+ INT32_TO_NPVARIANT(env->CallByteMethodA(object, id, parameters), *result);
+ break;
+ case JavaType::TypeChar:
+ INT32_TO_NPVARIANT(env->CallCharMethodA(object, id, parameters), *result);
+ break;
+ case JavaType::TypeShort:
+ INT32_TO_NPVARIANT(env->CallShortMethodA(object, id, parameters),
+ *result);
+ break;
+ case JavaType::TypeInt:
+ INT32_TO_NPVARIANT(env->CallIntMethodA(object, id, parameters), *result);
+ break;
+ case JavaType::TypeLong:
+ DOUBLE_TO_NPVARIANT(env->CallLongMethodA(object, id, parameters),
+ *result);
+ break;
+ case JavaType::TypeFloat:
+ DOUBLE_TO_NPVARIANT(env->CallFloatMethodA(object, id, parameters),
+ *result);
+ break;
+ case JavaType::TypeDouble:
+ DOUBLE_TO_NPVARIANT(env->CallDoubleMethodA(object, id, parameters),
+ *result);
+ break;
+ case JavaType::TypeVoid:
+ env->CallVoidMethodA(object, 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>(
+ env->CallObjectMethodA(object, 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 = env->CallObjectMethodA(object, 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),
+ *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) {
+ // 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);
+ return np_object;
+}
+
+JavaBoundObject::JavaBoundObject(
+ const JavaRef<jobject>& object,
+ const JavaRef<jclass>& safe_annotation_clazz,
+ const base::WeakPtr<JavaBridgeDispatcherHostManager>& manager)
+ : java_object_(AttachCurrentThread(), object.obj()),
+ manager_(manager),
+ are_methods_set_up_(false),
+ 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());
+}
+
+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;
+ }
+
+ // 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);
+ }
+
+ ScopedJavaLocalRef<jobject> obj = java_object_.get(AttachCurrentThread());
+
+ bool ok = false;
+ if (!obj.is_null()) {
+ // Call
+ ok = CallJNIMethod(obj.obj(), method->return_type(),
+ method->id(), &parameters[0], result,
+ safe_annotation_clazz_,
+ manager_);
+ }
+
+ // Now that we're done with the jvalue, release any local references created
+ // by CoerceJavaScriptValueToJavaValue().
+ JNIEnv* env = AttachCurrentThread();
+ for (size_t i = 0; i < arg_count; ++i) {
+ ReleaseJavaValueIfRequired(env, &parameters[i], method->parameter_type(i));
+ }
+
+ return ok;
+}
+
+void JavaBoundObject::EnsureMethodsAreSetUp() const {
+ if (are_methods_set_up_)
+ return;
+ are_methods_set_up_ = true;
+
+ JNIEnv* env = AttachCurrentThread();
+ ScopedJavaLocalRef<jobject> obj = java_object_.get(env);
+
+ if (obj.is_null()) {
+ return;
+ }
+
+ ScopedJavaLocalRef<jclass> clazz(env, static_cast<jclass>(
+ env->CallObjectMethod(obj.obj(), GetMethodIDFromClassName(
+ env,
+ kJavaLangObject,
+ kGetClass,
+ kReturningJavaLangClass))));
+
+ 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));
+ }
+}
+
+} // namespace content
diff --git a/chromium/content/browser/renderer_host/java/java_bound_object.h b/chromium/content/browser/renderer_host/java/java_bound_object.h
new file mode 100644
index 00000000000..ff97fdb2b6c
--- /dev/null
+++ b/chromium/content/browser/renderer_host/java/java_bound_object.h
@@ -0,0 +1,89 @@
+// 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.
+
+#ifndef CONTENT_BROWSER_RENDERER_HOST_JAVA_JAVA_BOUND_OBJECT_H_
+#define CONTENT_BROWSER_RENDERER_HOST_JAVA_JAVA_BOUND_OBJECT_H_
+
+#include <jni.h>
+#include <map>
+#include <string>
+
+#include "base/android/jni_helper.h"
+#include "base/android/scoped_java_ref.h"
+#include "base/memory/linked_ptr.h"
+#include "base/memory/weak_ptr.h"
+#include "content/browser/renderer_host/java/java_method.h"
+#include "third_party/npapi/bindings/npruntime.h"
+
+namespace content {
+
+class JavaBridgeDispatcherHostManager;
+
+// Wrapper around a Java object.
+//
+// Represents a Java object for use in the Java bridge. Holds a global ref to
+// the Java object and provides the ability to invoke methods on it.
+// Interrogation of the Java object for its methods is done lazily. This class
+// is not generally threadsafe. However, it does allow for instances to be
+// created and destroyed on different threads.
+class JavaBoundObject {
+ public:
+ // Takes a Java object and creates a JavaBoundObject around it. The
+ // |require_annotation| flag specifies whether or not only methods with the
+ // JavascriptInterface annotation are exposed to JavaScript. This property
+ // propagates to all Objects that get implicitly exposed as return values as
+ // well. Returns an NPObject with a ref count of one which owns the
+ // JavaBoundObject.
+ // See also comment below for |manager_|.
+ static NPObject* Create(
+ const base::android::JavaRef<jobject>& object,
+ const base::android::JavaRef<jclass>& safe_annotation_clazz,
+ const base::WeakPtr<JavaBridgeDispatcherHostManager>& manager);
+
+ virtual ~JavaBoundObject();
+
+ // Gets a local ref to the underlying JavaObject from a JavaBoundObject
+ // wrapped as an NPObject. May return null if the underlying object has
+ // been garbage collected.
+ static base::android::ScopedJavaLocalRef<jobject> GetJavaObject(
+ NPObject* object);
+
+ // Methods to implement the NPObject callbacks.
+ bool HasMethod(const std::string& name) const;
+ bool Invoke(const std::string& name, const NPVariant* args, size_t arg_count,
+ NPVariant* result);
+
+ private:
+ explicit JavaBoundObject(
+ const base::android::JavaRef<jobject>& object,
+ const base::android::JavaRef<jclass>& safe_annotation_clazz,
+ const base::WeakPtr<JavaBridgeDispatcherHostManager>& manager_);
+
+ void EnsureMethodsAreSetUp() const;
+
+ // The weak ref to the underlying Java object that this JavaBoundObject
+ // instance represents.
+ JavaObjectWeakGlobalRef java_object_;
+
+ // Keep a pointer back to the JavaBridgeDispatcherHostManager so that we
+ // can notify it when this JavaBoundObject is destroyed. JavaBoundObjects
+ // may outlive the manager so keep a WeakPtr. Note the WeakPtr may only be
+ // dereferenced on the UI thread.
+ base::WeakPtr<JavaBridgeDispatcherHostManager> manager_;
+
+ // Map of public methods, from method name to Method instance. Multiple
+ // entries will be present for overloaded methods. Note that we can't use
+ // scoped_ptr in STL containers as we can't copy it.
+ typedef std::multimap<std::string, linked_ptr<JavaMethod> > JavaMethodMap;
+ mutable JavaMethodMap methods_;
+ mutable bool are_methods_set_up_;
+
+ base::android::ScopedJavaGlobalRef<jclass> safe_annotation_clazz_;
+
+ DISALLOW_IMPLICIT_CONSTRUCTORS(JavaBoundObject);
+};
+
+} // namespace content
+
+#endif // CONTENT_BROWSER_RENDERER_HOST_JAVA_JAVA_BOUND_OBJECT_H_
diff --git a/chromium/content/browser/renderer_host/java/java_bridge_channel_host.cc b/chromium/content/browser/renderer_host/java/java_bridge_channel_host.cc
new file mode 100644
index 00000000000..8f53b9d99a8
--- /dev/null
+++ b/chromium/content/browser/renderer_host/java/java_bridge_channel_host.cc
@@ -0,0 +1,95 @@
+// Copyright (c) 2011 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_bridge_channel_host.h"
+
+#include "base/atomicops.h"
+#include "base/lazy_instance.h"
+#include "base/strings/stringprintf.h"
+#include "base/synchronization/waitable_event.h"
+#include "content/common/java_bridge_messages.h"
+
+using base::WaitableEvent;
+
+namespace content {
+namespace {
+struct WaitableEventLazyInstanceTraits
+ : public base::DefaultLazyInstanceTraits<WaitableEvent> {
+ static WaitableEvent* New(void* instance) {
+ // Use placement new to initialize our instance in our preallocated space.
+ // The parenthesis is very important here to force POD type initialization.
+ return new (instance) WaitableEvent(false, false);
+ }
+};
+base::LazyInstance<WaitableEvent, WaitableEventLazyInstanceTraits> dummy_event =
+ LAZY_INSTANCE_INITIALIZER;
+
+base::subtle::AtomicWord g_last_id = 0;
+}
+
+JavaBridgeChannelHost::~JavaBridgeChannelHost() {
+#if defined(OS_POSIX)
+ if (channel_handle_.socket.fd > 0) {
+ close(channel_handle_.socket.fd);
+ }
+#endif
+}
+
+JavaBridgeChannelHost* JavaBridgeChannelHost::GetJavaBridgeChannelHost(
+ int renderer_id,
+ base::MessageLoopProxy* ipc_message_loop) {
+ std::string channel_name(base::StringPrintf("r%d.javabridge", renderer_id));
+ // There's no need for a shutdown event here. If the browser is terminated
+ // while the JavaBridgeChannelHost is blocked on a synchronous IPC call, the
+ // renderer's shutdown event will cause the underlying channel to shut down,
+ // thus terminating the IPC call.
+ return static_cast<JavaBridgeChannelHost*>(NPChannelBase::GetChannel(
+ channel_name,
+ IPC::Channel::MODE_SERVER,
+ ClassFactory,
+ ipc_message_loop,
+ true,
+ dummy_event.Pointer()));
+}
+
+int JavaBridgeChannelHost::ThreadsafeGenerateRouteID() {
+ return base::subtle::NoBarrier_AtomicIncrement(&g_last_id, 1);
+}
+
+int JavaBridgeChannelHost::GenerateRouteID() {
+ return ThreadsafeGenerateRouteID();
+}
+
+bool JavaBridgeChannelHost::Init(base::MessageLoopProxy* ipc_message_loop,
+ bool create_pipe_now,
+ WaitableEvent* shutdown_event) {
+ if (!NPChannelBase::Init(ipc_message_loop, create_pipe_now, shutdown_event)) {
+ return false;
+ }
+
+ // Finish populating our ChannelHandle.
+#if defined(OS_POSIX)
+ // We take control of the FD for all session between this host and
+ // the corresponding renderers. We keep it open until this object
+ // is deleted.
+ channel_handle_.socket.fd = channel_->TakeClientFileDescriptor();
+#endif
+
+ return true;
+}
+
+bool JavaBridgeChannelHost::OnControlMessageReceived(
+ const IPC::Message& message) {
+ bool handled = true;
+ IPC_BEGIN_MESSAGE_MAP(JavaBridgeChannelHost, message)
+ IPC_MESSAGE_HANDLER(JavaBridgeMsg_GenerateRouteID, OnGenerateRouteID)
+ IPC_END_MESSAGE_MAP()
+ return handled;
+}
+
+void JavaBridgeChannelHost::OnGenerateRouteID(int* route_id) {
+ *route_id = GenerateRouteID();
+}
+
+} // namespace content
diff --git a/chromium/content/browser/renderer_host/java/java_bridge_channel_host.h b/chromium/content/browser/renderer_host/java/java_bridge_channel_host.h
new file mode 100644
index 00000000000..3f3f1468e9b
--- /dev/null
+++ b/chromium/content/browser/renderer_host/java/java_bridge_channel_host.h
@@ -0,0 +1,52 @@
+// 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.
+
+#ifndef CONTENT_BROWSER_RENDERER_HOST_JAVA_JAVA_BRIDGE_CHANNEL_HOST_H_
+#define CONTENT_BROWSER_RENDERER_HOST_JAVA_JAVA_BRIDGE_CHANNEL_HOST_H_
+
+#include "content/child/npapi/np_channel_base.h"
+
+namespace content {
+
+class JavaBridgeChannelHost : public NPChannelBase {
+ public:
+ static JavaBridgeChannelHost* GetJavaBridgeChannelHost(
+ int renderer_id,
+ base::MessageLoopProxy* ipc_message_loop);
+
+ // A threadsafe function to generate a unique route ID. Used by the
+ // JavaBridgeDispatcherHost on the UI thread and this class on the Java
+ // Bridge's background thread.
+ static int ThreadsafeGenerateRouteID();
+
+ // NPChannelBase implementation:
+ virtual int GenerateRouteID() OVERRIDE;
+
+ // NPChannelBase override:
+ virtual bool Init(base::MessageLoopProxy* ipc_message_loop,
+ bool create_pipe_now,
+ base::WaitableEvent* shutdown_event) OVERRIDE;
+
+ protected:
+ // NPChannelBase override:
+ virtual bool OnControlMessageReceived(const IPC::Message& message) OVERRIDE;
+
+ private:
+ JavaBridgeChannelHost() {}
+ friend class base::RefCountedThreadSafe<JavaBridgeChannelHost>;
+ virtual ~JavaBridgeChannelHost();
+
+ static NPChannelBase* ClassFactory() {
+ return new JavaBridgeChannelHost();
+ }
+
+ // Message handlers
+ void OnGenerateRouteID(int* route_id);
+
+ DISALLOW_COPY_AND_ASSIGN(JavaBridgeChannelHost);
+};
+
+} // namespace content
+
+#endif // CONTENT_BROWSER_RENDERER_HOST_JAVA_JAVA_BRIDGE_CHANNEL_HOST_H_
diff --git a/chromium/content/browser/renderer_host/java/java_bridge_dispatcher_host.cc b/chromium/content/browser/renderer_host/java/java_bridge_dispatcher_host.cc
new file mode 100644
index 00000000000..95a1c6aefe4
--- /dev/null
+++ b/chromium/content/browser/renderer_host/java/java_bridge_dispatcher_host.cc
@@ -0,0 +1,173 @@
+// 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_bridge_dispatcher_host.h"
+
+#include "base/android/java_handler_thread.h"
+#include "base/bind.h"
+#include "base/lazy_instance.h"
+#include "content/browser/renderer_host/java/java_bridge_channel_host.h"
+#include "content/browser/renderer_host/render_view_host_impl.h"
+#include "content/child/child_process.h"
+#include "content/child/npapi/npobject_stub.h"
+#include "content/child/npapi/npobject_util.h" // For CreateNPVariantParam()
+#include "content/common/java_bridge_messages.h"
+#include "content/public/browser/browser_thread.h"
+#include "content/public/browser/render_process_host.h"
+#include "third_party/WebKit/public/web/WebBindings.h"
+
+#if !defined(OS_ANDROID)
+#error "JavaBridge currently only supports OS_ANDROID"
+#endif
+
+namespace content {
+
+namespace {
+// The JavaBridge needs to use a Java thread so the callback
+// will happen on a thread with a prepared Looper.
+class JavaBridgeThread : public base::android::JavaHandlerThread {
+ public:
+ JavaBridgeThread() : base::android::JavaHandlerThread("JavaBridge") {
+ Start();
+ }
+ virtual ~JavaBridgeThread() {
+ Stop();
+ }
+};
+
+void CleanUpStubs(const std::vector<base::WeakPtr<NPObjectStub> > & stubs) {
+ for (size_t i = 0; i < stubs.size(); ++i) {
+ if (stubs[i]) {
+ stubs[i]->DeleteSoon();
+ }
+ }
+}
+
+base::LazyInstance<JavaBridgeThread> g_background_thread =
+ LAZY_INSTANCE_INITIALIZER;
+} // namespace
+
+JavaBridgeDispatcherHost::JavaBridgeDispatcherHost(
+ RenderViewHost* render_view_host)
+ : RenderViewHostObserver(render_view_host),
+ is_renderer_initialized_(false) {
+}
+
+JavaBridgeDispatcherHost::~JavaBridgeDispatcherHost() {
+ g_background_thread.Get().message_loop()->PostTask(
+ FROM_HERE,
+ base::Bind(&CleanUpStubs, stubs_));
+}
+
+void JavaBridgeDispatcherHost::AddNamedObject(const string16& name,
+ NPObject* object) {
+ NPVariant_Param variant_param;
+ CreateNPVariantParam(object, &variant_param);
+
+ if (!is_renderer_initialized_) {
+ is_renderer_initialized_ = true;
+ Send(new JavaBridgeMsg_Init(routing_id()));
+ }
+ Send(new JavaBridgeMsg_AddNamedObject(routing_id(), name, variant_param));
+}
+
+void JavaBridgeDispatcherHost::RemoveNamedObject(const string16& name) {
+ // On receipt of this message, the JavaBridgeDispatcher will drop its
+ // reference to the corresponding proxy object. When the last reference is
+ // removed, the proxy object will delete its NPObjectProxy, which will cause
+ // the NPObjectStub to be deleted, which will drop its reference to the
+ // original NPObject.
+ Send(new JavaBridgeMsg_RemoveNamedObject(routing_id(), name));
+}
+
+bool JavaBridgeDispatcherHost::Send(IPC::Message* msg) {
+ return RenderViewHostObserver::Send(msg);
+}
+
+void JavaBridgeDispatcherHost::RenderViewHostDestroyed(
+ RenderViewHost* render_view_host) {
+ // Base implementation deletes the object. This class is ref counted, with
+ // refs held by the JavaBridgeDispatcherHostManager and base::Bind, so that
+ // behavior is unwanted.
+}
+
+bool JavaBridgeDispatcherHost::OnMessageReceived(const IPC::Message& msg) {
+ bool handled = true;
+ IPC_BEGIN_MESSAGE_MAP(JavaBridgeDispatcherHost, msg)
+ IPC_MESSAGE_HANDLER_DELAY_REPLY(JavaBridgeHostMsg_GetChannelHandle,
+ OnGetChannelHandle)
+ IPC_MESSAGE_UNHANDLED(handled = false)
+ IPC_END_MESSAGE_MAP()
+ return handled;
+}
+
+void JavaBridgeDispatcherHost::OnGetChannelHandle(IPC::Message* reply_msg) {
+ g_background_thread.Get().message_loop()->PostTask(
+ FROM_HERE,
+ base::Bind(&JavaBridgeDispatcherHost::GetChannelHandle, this, reply_msg));
+}
+
+void JavaBridgeDispatcherHost::GetChannelHandle(IPC::Message* reply_msg) {
+ // The channel creates the channel handle based on the renderer ID we passed
+ // to GetJavaBridgeChannelHost() and, on POSIX, the file descriptor used by
+ // the underlying channel.
+ JavaBridgeHostMsg_GetChannelHandle::WriteReplyParams(
+ reply_msg,
+ channel_->channel_handle());
+ Send(reply_msg);
+}
+
+void JavaBridgeDispatcherHost::CreateNPVariantParam(NPObject* object,
+ NPVariant_Param* param) {
+ // The JavaBridgeChannelHost needs to be created on the background thread, as
+ // that is where Java objects will live, and CreateNPVariantParam() needs the
+ // channel to create the NPObjectStub. To avoid blocking here until the
+ // channel is ready, create the NPVariant_Param by hand, then post a message
+ // to the background thread to set up the channel and create the corresponding
+ // NPObjectStub. Post that message before doing any IPC, to make sure that
+ // the channel and object proxies are ready before responses are received
+ // from the renderer.
+
+ // Create an NPVariantParam suitable for serialization over IPC from our
+ // NPVariant. See CreateNPVariantParam() in npobject_utils.
+ param->type = NPVARIANT_PARAM_SENDER_OBJECT_ROUTING_ID;
+ int route_id = JavaBridgeChannelHost::ThreadsafeGenerateRouteID();
+ param->npobject_routing_id = route_id;
+
+ WebKit::WebBindings::retainObject(object);
+ g_background_thread.Get().message_loop()->PostTask(
+ FROM_HERE,
+ base::Bind(&JavaBridgeDispatcherHost::CreateObjectStub, this, object,
+ route_id));
+}
+
+void JavaBridgeDispatcherHost::CreateObjectStub(NPObject* object,
+ int route_id) {
+ DCHECK_EQ(g_background_thread.Get().message_loop(),
+ base::MessageLoop::current());
+ if (!channel_.get()) {
+ channel_ = JavaBridgeChannelHost::GetJavaBridgeChannelHost(
+ render_view_host()->GetProcess()->GetID(),
+ BrowserThread::GetMessageLoopProxyForThread(BrowserThread::IO));
+ }
+
+ // In a typical scenario, the lifetime of each NPObjectStub is governed by
+ // that of the NPObjectProxy in the renderer, via the channel. However,
+ // we cannot guaranteed that the renderer always terminates cleanly
+ // (crashes / sometimes just unavoidable). We keep a weak reference to
+ // it now and schedule a delete on it when this host is getting deleted.
+
+ // Pass 0 for the containing window, as it's only used by plugins to pump the
+ // window message queue when a method on a renderer-side object causes a
+ // dialog to be displayed, and the Java Bridge does not need this
+ // functionality. The page URL is also not required.
+ stubs_.push_back((new NPObjectStub(
+ object, channel_.get(), route_id, 0, GURL()))->AsWeakPtr());
+
+ // The NPObjectStub takes a reference to the NPObject. Release the ref added
+ // in CreateNPVariantParam().
+ WebKit::WebBindings::releaseObject(object);
+}
+
+} // namespace content
diff --git a/chromium/content/browser/renderer_host/java/java_bridge_dispatcher_host.h b/chromium/content/browser/renderer_host/java/java_bridge_dispatcher_host.h
new file mode 100644
index 00000000000..6d44c39d22a
--- /dev/null
+++ b/chromium/content/browser/renderer_host/java/java_bridge_dispatcher_host.h
@@ -0,0 +1,75 @@
+// 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.
+
+#ifndef CONTENT_BROWSER_RENDERER_HOST_JAVA_JAVA_BRIDGE_DISPATCHER_HOST_H_
+#define CONTENT_BROWSER_RENDERER_HOST_JAVA_JAVA_BRIDGE_DISPATCHER_HOST_H_
+
+#include <vector>
+#include "base/memory/ref_counted.h"
+#include "base/memory/weak_ptr.h"
+#include "base/strings/string16.h"
+#include "content/child/npapi/npobject_stub.h"
+#include "content/public/browser/render_view_host_observer.h"
+
+class RouteIDGenerator;
+struct NPObject;
+
+namespace content {
+class NPChannelBase;
+class RenderViewHost;
+struct NPVariant_Param;
+
+// This class handles injecting Java objects into a single RenderView. The Java
+// object itself lives in the browser process on a background thread, while a
+// proxy object is created in the renderer. An instance of this class exists
+// for each RenderViewHost.
+class JavaBridgeDispatcherHost
+ : public base::RefCountedThreadSafe<JavaBridgeDispatcherHost>,
+ public RenderViewHostObserver {
+ public:
+ // We hold a weak pointer to the RenderViewhost. It must outlive this object.
+ JavaBridgeDispatcherHost(RenderViewHost* render_view_host);
+
+ // Injects |object| into the main frame of the corresponding RenderView. A
+ // proxy object is created in the renderer and when the main frame's window
+ // object is next cleared, this proxy object is bound to the window object
+ // using |name|. The proxy object remains bound until the next time the
+ // window object is cleared after a call to RemoveNamedObject() or
+ // AddNamedObject() with the same name. The proxy object proxies calls back
+ // to |object|, which is manipulated on the background thread. This class
+ // holds a reference to |object| for the time that the proxy object is bound
+ // to the window object.
+ void AddNamedObject(const string16& name, NPObject* object);
+ void RemoveNamedObject(const string16& name);
+
+ // RenderViewHostObserver overrides:
+ // The IPC macros require this to be public.
+ virtual bool Send(IPC::Message* msg) OVERRIDE;
+ virtual void RenderViewHostDestroyed(
+ RenderViewHost* render_view_host) OVERRIDE;
+
+ private:
+ friend class base::RefCountedThreadSafe<JavaBridgeDispatcherHost>;
+ virtual ~JavaBridgeDispatcherHost();
+
+ // RenderViewHostObserver override:
+ virtual bool OnMessageReceived(const IPC::Message& message) OVERRIDE;
+
+ // Message handlers
+ void OnGetChannelHandle(IPC::Message* reply_msg);
+
+ void GetChannelHandle(IPC::Message* reply_msg);
+ void CreateNPVariantParam(NPObject* object, NPVariant_Param* param);
+ void CreateObjectStub(NPObject* object, int route_id);
+
+ scoped_refptr<NPChannelBase> channel_;
+ bool is_renderer_initialized_;
+ std::vector<base::WeakPtr<NPObjectStub> > stubs_;
+
+ DISALLOW_COPY_AND_ASSIGN(JavaBridgeDispatcherHost);
+};
+
+} // namespace content
+
+#endif // CONTENT_BROWSER_RENDERER_HOST_JAVA_JAVA_BRIDGE_DISPATCHER_HOST_H_
diff --git a/chromium/content/browser/renderer_host/java/java_bridge_dispatcher_host_manager.cc b/chromium/content/browser/renderer_host/java/java_bridge_dispatcher_host_manager.cc
new file mode 100644
index 00000000000..bac38564e79
--- /dev/null
+++ b/chromium/content/browser/renderer_host/java/java_bridge_dispatcher_host_manager.cc
@@ -0,0 +1,156 @@
+// 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_bridge_dispatcher_host_manager.h"
+
+#include "base/android/jni_android.h"
+#include "base/android/jni_helper.h"
+#include "base/android/scoped_java_ref.h"
+#include "base/bind.h"
+#include "base/logging.h"
+#include "base/strings/utf_string_conversions.h"
+#include "content/browser/renderer_host/java/java_bound_object.h"
+#include "content/browser/renderer_host/java/java_bridge_dispatcher_host.h"
+#include "content/common/android/hash_set.h"
+#include "content/public/browser/browser_thread.h"
+#include "third_party/WebKit/public/web/WebBindings.h"
+
+namespace content {
+
+JavaBridgeDispatcherHostManager::JavaBridgeDispatcherHostManager(
+ WebContents* web_contents)
+ : WebContentsObserver(web_contents) {
+}
+
+JavaBridgeDispatcherHostManager::~JavaBridgeDispatcherHostManager() {
+ for (ObjectMap::iterator iter = objects_.begin(); iter != objects_.end();
+ ++iter) {
+ WebKit::WebBindings::releaseObject(iter->second);
+ }
+ DCHECK_EQ(0U, instances_.size());
+}
+
+void JavaBridgeDispatcherHostManager::AddNamedObject(const string16& name,
+ NPObject* object) {
+ // Record this object in a map so that we can add it into RenderViewHosts
+ // created later. The JavaBridgeDispatcherHost instances will take a
+ // reference to the object, but we take one too, because this method can be
+ // called before there are any such instances.
+ WebKit::WebBindings::retainObject(object);
+ objects_[name] = object;
+
+ for (InstanceMap::iterator iter = instances_.begin();
+ iter != instances_.end(); ++iter) {
+ iter->second->AddNamedObject(name, object);
+ }
+}
+
+void JavaBridgeDispatcherHostManager::SetRetainedObjectSet(
+ const JavaObjectWeakGlobalRef& retained_object_set) {
+ // It's an error to replace the retained_object_set_ after it's been set,
+ // so we check that it hasn't already been here.
+ // TODO(benm): It'd be better to pass the set in the constructor to avoid
+ // the chance of this happening; but that's tricky as this get's constructed
+ // before ContentViewCore (which owns the set). Best solution may be to move
+ // ownership of the JavaBridgerDispatchHostManager from WebContents to
+ // ContentViewCore?
+ JNIEnv* env = base::android::AttachCurrentThread();
+ base::android::ScopedJavaLocalRef<jobject> new_retained_object_set =
+ retained_object_set.get(env);
+ base::android::ScopedJavaLocalRef<jobject> current_retained_object_set =
+ retained_object_set_.get(env);
+ if (!env->IsSameObject(new_retained_object_set.obj(),
+ current_retained_object_set.obj())) {
+ DCHECK(current_retained_object_set.is_null());
+ retained_object_set_ = retained_object_set;
+ }
+}
+
+void JavaBridgeDispatcherHostManager::RemoveNamedObject(const string16& name) {
+ ObjectMap::iterator iter = objects_.find(name);
+ if (iter == objects_.end()) {
+ return;
+ }
+
+ WebKit::WebBindings::releaseObject(iter->second);
+ objects_.erase(iter);
+
+ for (InstanceMap::iterator iter = instances_.begin();
+ iter != instances_.end(); ++iter) {
+ iter->second->RemoveNamedObject(name);
+ }
+}
+
+void JavaBridgeDispatcherHostManager::RenderViewCreated(
+ RenderViewHost* render_view_host) {
+ // Creates a JavaBridgeDispatcherHost for the specified RenderViewHost and
+ // adds all currently registered named objects to the new instance.
+ scoped_refptr<JavaBridgeDispatcherHost> instance =
+ new JavaBridgeDispatcherHost(render_view_host);
+
+ for (ObjectMap::const_iterator iter = objects_.begin();
+ iter != objects_.end(); ++iter) {
+ instance->AddNamedObject(iter->first, iter->second);
+ }
+
+ instances_[render_view_host] = instance;
+}
+
+void JavaBridgeDispatcherHostManager::RenderViewDeleted(
+ RenderViewHost* render_view_host) {
+ instances_.erase(render_view_host);
+}
+
+void JavaBridgeDispatcherHostManager::WebContentsDestroyed(
+ WebContents* web_contents) {
+ // When a WebContents is shutting down, it clears its observers before
+ // it kills all of its RenderViewHosts, so we won't get a call to
+ // RenderViewDeleted() for all RenderViewHosts.
+ instances_.clear();
+}
+
+void JavaBridgeDispatcherHostManager::DocumentAvailableInMainFrame() {
+ DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+ // Called when the window object has been cleared in the main frame.
+ JNIEnv* env = base::android::AttachCurrentThread();
+ base::android::ScopedJavaLocalRef<jobject> retained_object_set =
+ retained_object_set_.get(env);
+ if (!retained_object_set.is_null()) {
+ JNI_Java_HashSet_clear(env, retained_object_set);
+
+ // We also need to add back the named objects we have so far as they
+ // should survive navigations.
+ ObjectMap::iterator it = objects_.begin();
+ for (; it != objects_.end(); ++it) {
+ JNI_Java_HashSet_add(env, retained_object_set,
+ JavaBoundObject::GetJavaObject(it->second));
+ }
+ }
+}
+
+void JavaBridgeDispatcherHostManager::JavaBoundObjectCreated(
+ const base::android::JavaRef<jobject>& object) {
+ DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+
+ JNIEnv* env = base::android::AttachCurrentThread();
+ base::android::ScopedJavaLocalRef<jobject> retained_object_set =
+ retained_object_set_.get(env);
+ if (!retained_object_set.is_null()) {
+ JNI_Java_HashSet_add(env, retained_object_set, object);
+ }
+}
+
+void JavaBridgeDispatcherHostManager::JavaBoundObjectDestroyed(
+ const base::android::JavaRef<jobject>& object) {
+ DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
+
+ JNIEnv* env = base::android::AttachCurrentThread();
+ base::android::ScopedJavaLocalRef<jobject> retained_object_set =
+ retained_object_set_.get(env);
+ if (!retained_object_set.is_null()) {
+ JNI_Java_HashSet_remove(env, retained_object_set, object);
+ }
+}
+
+} // namespace content
diff --git a/chromium/content/browser/renderer_host/java/java_bridge_dispatcher_host_manager.h b/chromium/content/browser/renderer_host/java/java_bridge_dispatcher_host_manager.h
new file mode 100644
index 00000000000..6fb7aab7cfb
--- /dev/null
+++ b/chromium/content/browser/renderer_host/java/java_bridge_dispatcher_host_manager.h
@@ -0,0 +1,70 @@
+// 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.
+
+#ifndef CONTENT_BROWSER_RENDERER_HOST_JAVA_JAVA_BRIDGE_DISPATCHER_HOST_MANAGER_H_
+#define CONTENT_BROWSER_RENDERER_HOST_JAVA_JAVA_BRIDGE_DISPATCHER_HOST_MANAGER_H_
+
+#include <map>
+
+#include "base/android/jni_helper.h"
+#include "base/android/scoped_java_ref.h"
+#include "base/compiler_specific.h"
+#include "base/memory/ref_counted.h"
+#include "base/memory/weak_ptr.h"
+#include "base/strings/string16.h"
+#include "content/public/browser/web_contents_observer.h"
+
+struct NPObject;
+
+namespace content {
+class JavaBridgeDispatcherHost;
+class RenderViewHost;
+
+// This class handles injecting Java objects into all of the RenderViews
+// associated with a WebContents. It manages a set of JavaBridgeDispatcherHost
+// objects, one per RenderViewHost.
+class JavaBridgeDispatcherHostManager
+ : public WebContentsObserver,
+ public base::SupportsWeakPtr<JavaBridgeDispatcherHostManager> {
+ public:
+ explicit JavaBridgeDispatcherHostManager(WebContents* web_contents);
+ virtual ~JavaBridgeDispatcherHostManager();
+
+ // These methods add or remove the object to each JavaBridgeDispatcherHost.
+ // Each one holds a reference to the NPObject while the object is bound to
+ // the corresponding RenderView. See JavaBridgeDispatcherHost for details.
+ void AddNamedObject(const string16& name, NPObject* object);
+ void RemoveNamedObject(const string16& name);
+
+ // Every time a JavaBoundObject backed by a real Java object is
+ // created/destroyed, we insert/remove a strong ref to that Java object into
+ // this set so that it doesn't get garbage collected while it's still
+ // potentially in use. Although the set is managed native side, it's owned
+ // and defined in Java so that pushing refs into it does not create new GC
+ // roots that would prevent ContentViewCore from being garbage collected.
+ void SetRetainedObjectSet(const JavaObjectWeakGlobalRef& retained_object_set);
+
+ // WebContentsObserver overrides
+ virtual void RenderViewCreated(RenderViewHost* render_view_host) OVERRIDE;
+ virtual void RenderViewDeleted(RenderViewHost* render_view_host) OVERRIDE;
+ virtual void WebContentsDestroyed(WebContents* web_contents) OVERRIDE;
+ virtual void DocumentAvailableInMainFrame() OVERRIDE;
+
+ void JavaBoundObjectCreated(const base::android::JavaRef<jobject>& object);
+ void JavaBoundObjectDestroyed(const base::android::JavaRef<jobject>& object);
+
+ private:
+ typedef std::map<RenderViewHost*, scoped_refptr<JavaBridgeDispatcherHost> >
+ InstanceMap;
+ InstanceMap instances_;
+ typedef std::map<string16, NPObject*> ObjectMap;
+ ObjectMap objects_;
+ JavaObjectWeakGlobalRef retained_object_set_;
+
+ DISALLOW_COPY_AND_ASSIGN(JavaBridgeDispatcherHostManager);
+};
+
+} // namespace content
+
+#endif // CONTENT_BROWSER_RENDERER_HOST_JAVA_JAVA_BRIDGE_DISPATCHER_HOST_MANAGER_H_
diff --git a/chromium/content/browser/renderer_host/java/java_method.cc b/chromium/content/browser/renderer_host/java/java_method.cc
new file mode 100644
index 00000000000..3cc569fbcae
--- /dev/null
+++ b/chromium/content/browser/renderer_host/java/java_method.cc
@@ -0,0 +1,237 @@
+// 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_method.h"
+
+#include "base/android/jni_android.h"
+#include "base/android/jni_string.h"
+#include "base/lazy_instance.h"
+#include "base/memory/singleton.h"
+#include "base/strings/string_util.h" // For ReplaceSubstringsAfterOffset
+
+using base::android::AttachCurrentThread;
+using base::android::ConvertJavaStringToUTF8;
+using base::android::GetClass;
+using base::android::GetMethodIDFromClassName;
+using base::android::MethodID;
+using base::android::ScopedJavaGlobalRef;
+using base::android::ScopedJavaLocalRef;
+
+namespace content {
+namespace {
+
+const char kGetName[] = "getName";
+const char kGetDeclaringClass[] = "getDeclaringClass";
+const char kGetModifiers[] = "getModifiers";
+const char kGetParameterTypes[] = "getParameterTypes";
+const char kGetReturnType[] = "getReturnType";
+const char kIntegerReturningBoolean[] = "(I)Z";
+const char kIsStatic[] = "isStatic";
+const char kJavaLangClass[] = "java/lang/Class";
+const char kJavaLangReflectMethod[] = "java/lang/reflect/Method";
+const char kJavaLangReflectModifier[] = "java/lang/reflect/Modifier";
+const char kReturningInteger[] = "()I";
+const char kReturningJavaLangClass[] = "()Ljava/lang/Class;";
+const char kReturningJavaLangClassArray[] = "()[Ljava/lang/Class;";
+const char kReturningJavaLangString[] = "()Ljava/lang/String;";
+
+struct ModifierClassTraits :
+ public base::internal::LeakyLazyInstanceTraits<ScopedJavaGlobalRef<
+ jclass> > {
+ static ScopedJavaGlobalRef<jclass>* New(void* instance) {
+ JNIEnv* env = AttachCurrentThread();
+ // Use placement new to initialize our instance in our preallocated space.
+ return new (instance) ScopedJavaGlobalRef<jclass>(
+ GetClass(env, kJavaLangReflectModifier));
+ }
+};
+
+base::LazyInstance<ScopedJavaGlobalRef<jclass>, ModifierClassTraits>
+ g_java_lang_reflect_modifier_class = LAZY_INSTANCE_INITIALIZER;
+
+std::string BinaryNameToJNIName(const std::string& binary_name,
+ JavaType* type) {
+ DCHECK(type);
+ *type = JavaType::CreateFromBinaryName(binary_name);
+ switch (type->type) {
+ case JavaType::TypeBoolean:
+ return "Z";
+ case JavaType::TypeByte:
+ return "B";
+ case JavaType::TypeChar:
+ return "C";
+ case JavaType::TypeShort:
+ return "S";
+ case JavaType::TypeInt:
+ return "I";
+ case JavaType::TypeLong:
+ return "J";
+ case JavaType::TypeFloat:
+ return "F";
+ case JavaType::TypeDouble:
+ return "D";
+ case JavaType::TypeVoid:
+ return "V";
+ case JavaType::TypeArray: {
+ // For array types, the binary name uses the JNI name encodings.
+ std::string jni_name = binary_name;
+ ReplaceSubstringsAfterOffset(&jni_name, 0, ".", "/");
+ return jni_name;
+ }
+ case JavaType::TypeString:
+ case JavaType::TypeObject:
+ std::string jni_name = "L" + binary_name + ";";
+ ReplaceSubstringsAfterOffset(&jni_name, 0, ".", "/");
+ return jni_name;
+ }
+ NOTREACHED();
+ return EmptyString();
+}
+
+} // namespace
+
+JavaMethod::JavaMethod(const base::android::JavaRef<jobject>& method)
+ : java_method_(method),
+ have_calculated_num_parameters_(false),
+ id_(NULL) {
+ JNIEnv* env = AttachCurrentThread();
+ // On construction, we do nothing except get the name. Everything else is
+ // done lazily.
+ ScopedJavaLocalRef<jstring> name(env, static_cast<jstring>(
+ env->CallObjectMethod(java_method_.obj(), GetMethodIDFromClassName(
+ env,
+ kJavaLangReflectMethod,
+ kGetName,
+ kReturningJavaLangString))));
+ name_ = ConvertJavaStringToUTF8(name);
+}
+
+JavaMethod::~JavaMethod() {
+}
+
+size_t JavaMethod::num_parameters() const {
+ EnsureNumParametersIsSetUp();
+ return num_parameters_;
+}
+
+const JavaType& JavaMethod::parameter_type(size_t index) const {
+ EnsureTypesAndIDAreSetUp();
+ return parameter_types_[index];
+}
+
+const JavaType& JavaMethod::return_type() const {
+ EnsureTypesAndIDAreSetUp();
+ return return_type_;
+}
+
+jmethodID JavaMethod::id() const {
+ EnsureTypesAndIDAreSetUp();
+ return id_;
+}
+
+void JavaMethod::EnsureNumParametersIsSetUp() const {
+ if (have_calculated_num_parameters_) {
+ return;
+ }
+ have_calculated_num_parameters_ = true;
+
+ // The number of parameters will be used frequently when determining
+ // whether to call this method. We don't get the ID etc until actually
+ // required.
+ JNIEnv* env = AttachCurrentThread();
+ ScopedJavaLocalRef<jarray> parameters(env, static_cast<jarray>(
+ env->CallObjectMethod(java_method_.obj(), GetMethodIDFromClassName(
+ env,
+ kJavaLangReflectMethod,
+ kGetParameterTypes,
+ kReturningJavaLangClassArray))));
+ num_parameters_ = env->GetArrayLength(parameters.obj());
+}
+
+void JavaMethod::EnsureTypesAndIDAreSetUp() const {
+ if (id_) {
+ return;
+ }
+
+ // Get the parameters
+ JNIEnv* env = AttachCurrentThread();
+ ScopedJavaLocalRef<jobjectArray> parameters(env, static_cast<jobjectArray>(
+ env->CallObjectMethod(java_method_.obj(), GetMethodIDFromClassName(
+ env,
+ kJavaLangReflectMethod,
+ kGetParameterTypes,
+ kReturningJavaLangClassArray))));
+ // Usually, this will already have been called.
+ EnsureNumParametersIsSetUp();
+ DCHECK_EQ(num_parameters_,
+ static_cast<size_t>(env->GetArrayLength(parameters.obj())));
+
+ // Java gives us the argument type using an extended version of the 'binary
+ // name'. See
+ // http://download.oracle.com/javase/1.4.2/docs/api/java/lang/Class.html#getName().
+ // If we build the signature now, there's no need to store the binary name
+ // of the arguments. We just store the simple type.
+ std::string signature("(");
+
+ // Form the signature and record the parameter types.
+ parameter_types_.resize(num_parameters_);
+ for (size_t i = 0; i < num_parameters_; ++i) {
+ ScopedJavaLocalRef<jobject> parameter(env, env->GetObjectArrayElement(
+ parameters.obj(), i));
+ ScopedJavaLocalRef<jstring> name(env, static_cast<jstring>(
+ env->CallObjectMethod(parameter.obj(), GetMethodIDFromClassName(
+ env,
+ kJavaLangClass,
+ kGetName,
+ kReturningJavaLangString))));
+ std::string name_utf8 = ConvertJavaStringToUTF8(name);
+ signature += BinaryNameToJNIName(name_utf8, &parameter_types_[i]);
+ }
+ signature += ")";
+
+ // Get the return type
+ ScopedJavaLocalRef<jclass> clazz(env, static_cast<jclass>(
+ env->CallObjectMethod(java_method_.obj(), GetMethodIDFromClassName(
+ env,
+ kJavaLangReflectMethod,
+ kGetReturnType,
+ kReturningJavaLangClass))));
+ ScopedJavaLocalRef<jstring> name(env, static_cast<jstring>(
+ env->CallObjectMethod(clazz.obj(), GetMethodIDFromClassName(
+ env,
+ kJavaLangClass,
+ kGetName,
+ kReturningJavaLangString))));
+ signature += BinaryNameToJNIName(ConvertJavaStringToUTF8(name),
+ &return_type_);
+
+ // Determine whether the method is static.
+ jint modifiers = env->CallIntMethod(
+ java_method_.obj(), GetMethodIDFromClassName(env,
+ kJavaLangReflectMethod,
+ kGetModifiers,
+ kReturningInteger));
+ bool is_static = env->CallStaticBooleanMethod(
+ g_java_lang_reflect_modifier_class.Get().obj(),
+ MethodID::Get<MethodID::TYPE_STATIC>(
+ env, g_java_lang_reflect_modifier_class.Get().obj(), kIsStatic,
+ kIntegerReturningBoolean),
+ modifiers);
+
+ // Get the ID for this method.
+ ScopedJavaLocalRef<jclass> declaring_class(env, static_cast<jclass>(
+ env->CallObjectMethod(java_method_.obj(), GetMethodIDFromClassName(
+ env,
+ kJavaLangReflectMethod,
+ kGetDeclaringClass,
+ kReturningJavaLangClass))));
+ id_ = is_static ?
+ MethodID::Get<MethodID::TYPE_STATIC>(
+ env, declaring_class.obj(), name_.c_str(), signature.c_str()) :
+ MethodID::Get<MethodID::TYPE_INSTANCE>(
+ env, declaring_class.obj(), name_.c_str(), signature.c_str());
+ java_method_.Reset();
+}
+
+} // namespace content
diff --git a/chromium/content/browser/renderer_host/java/java_method.h b/chromium/content/browser/renderer_host/java/java_method.h
new file mode 100644
index 00000000000..6356f70069d
--- /dev/null
+++ b/chromium/content/browser/renderer_host/java/java_method.h
@@ -0,0 +1,47 @@
+// Copyright (c) 2011 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.
+
+#ifndef CONTENT_BROWSER_RENDERER_HOST_JAVA_JAVA_METHOD_H_
+#define CONTENT_BROWSER_RENDERER_HOST_JAVA_JAVA_METHOD_H_
+
+#include <jni.h>
+#include <string>
+#include <vector>
+
+#include "base/android/scoped_java_ref.h"
+#include "content/browser/renderer_host/java/java_type.h"
+
+namespace content {
+
+// Wrapper around java.lang.reflect.Method. This class must be used on a single
+// thread only.
+class JavaMethod {
+ public:
+ explicit JavaMethod(const base::android::JavaRef<jobject>& method);
+ ~JavaMethod();
+
+ const std::string& name() const { return name_; }
+ size_t num_parameters() const;
+ const JavaType& parameter_type(size_t index) const;
+ const JavaType& return_type() const;
+ jmethodID id() const;
+
+ private:
+ void EnsureNumParametersIsSetUp() const;
+ void EnsureTypesAndIDAreSetUp() const;
+
+ std::string name_;
+ mutable base::android::ScopedJavaGlobalRef<jobject> java_method_;
+ mutable bool have_calculated_num_parameters_;
+ mutable size_t num_parameters_;
+ mutable std::vector<JavaType> parameter_types_;
+ mutable JavaType return_type_;
+ mutable jmethodID id_;
+
+ DISALLOW_IMPLICIT_CONSTRUCTORS(JavaMethod);
+};
+
+} // namespace content
+
+#endif // CONTENT_BROWSER_RENDERER_HOST_JAVA_JAVA_METHOD_H_
diff --git a/chromium/content/browser/renderer_host/java/java_type.cc b/chromium/content/browser/renderer_host/java/java_type.cc
new file mode 100644
index 00000000000..b590e7733c0
--- /dev/null
+++ b/chromium/content/browser/renderer_host/java/java_type.cc
@@ -0,0 +1,114 @@
+// 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_type.h"
+
+#include "base/logging.h"
+
+namespace content {
+namespace {
+
+JavaType JavaTypeFromJNIName(const std::string& jni_name) {
+ JavaType result;
+ DCHECK(!jni_name.empty());
+ switch (jni_name[0]) {
+ case 'Z':
+ result.type = JavaType::TypeBoolean;
+ break;
+ case 'B':
+ result.type = JavaType::TypeByte;
+ break;
+ case 'C':
+ result.type = JavaType::TypeChar;
+ break;
+ case 'S':
+ result.type = JavaType::TypeShort;
+ break;
+ case 'I':
+ result.type = JavaType::TypeInt;
+ break;
+ case 'J':
+ result.type = JavaType::TypeLong;
+ break;
+ case 'F':
+ result.type = JavaType::TypeFloat;
+ break;
+ case 'D':
+ result.type = JavaType::TypeDouble;
+ break;
+ case '[':
+ result.type = JavaType::TypeArray;
+ // LIVECONNECT_COMPLIANCE: We don't support multi-dimensional arrays, so
+ // there's no need to populate the inner types.
+ break;
+ case 'L':
+ result.type = jni_name == "Ljava.lang.String;" ?
+ JavaType::TypeString :
+ JavaType::TypeObject;
+ break;
+ default:
+ // Includes void (V).
+ NOTREACHED();
+ }
+ return result;
+}
+
+} // namespace
+
+JavaType::JavaType() {
+}
+
+JavaType::JavaType(const JavaType& other) {
+ *this = other;
+}
+
+JavaType::~JavaType() {
+}
+
+JavaType& JavaType::operator=(const JavaType& other) {
+ type = other.type;
+ if (other.inner_type) {
+ DCHECK_EQ(JavaType::TypeArray, type);
+ inner_type.reset(new JavaType(*other.inner_type));
+ } else {
+ inner_type.reset();
+ }
+ return *this;
+}
+
+JavaType JavaType::CreateFromBinaryName(const std::string& binary_name) {
+ JavaType result;
+ DCHECK(!binary_name.empty());
+ if (binary_name == "boolean") {
+ result.type = JavaType::TypeBoolean;
+ } else if (binary_name == "byte") {
+ result.type = JavaType::TypeByte;
+ } else if (binary_name == "char") {
+ result.type = JavaType::TypeChar;
+ } else if (binary_name == "short") {
+ result.type = JavaType::TypeShort;
+ } else if (binary_name == "int") {
+ result.type = JavaType::TypeInt;
+ } else if (binary_name == "long") {
+ result.type = JavaType::TypeLong;
+ } else if (binary_name == "float") {
+ result.type = JavaType::TypeFloat;
+ } else if (binary_name == "double") {
+ result.type = JavaType::TypeDouble;
+ } else if (binary_name == "void") {
+ result.type = JavaType::TypeVoid;
+ } else if (binary_name[0] == '[') {
+ result.type = JavaType::TypeArray;
+ // The inner type of an array is represented in JNI format.
+ result.inner_type.reset(new JavaType(JavaTypeFromJNIName(
+ binary_name.substr(1))));
+ } else if (binary_name == "java.lang.String") {
+ result.type = JavaType::TypeString;
+ } else {
+ result.type = JavaType::TypeObject;
+ }
+ return result;
+}
+
+} // namespace content
diff --git a/chromium/content/browser/renderer_host/java/java_type.h b/chromium/content/browser/renderer_host/java/java_type.h
new file mode 100644
index 00000000000..fe7845eeaa6
--- /dev/null
+++ b/chromium/content/browser/renderer_host/java/java_type.h
@@ -0,0 +1,50 @@
+// 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.
+
+#ifndef CONTENT_BROWSER_RENDERER_HOST_JAVA_JAVA_TYPE_H_
+#define CONTENT_BROWSER_RENDERER_HOST_JAVA_JAVA_TYPE_H_
+
+#include <string>
+
+#include "base/memory/scoped_ptr.h"
+
+namespace content {
+
+// The type of a Java value. A light-weight enum-like structure intended for
+// use by value and in STL containers.
+struct JavaType {
+ JavaType();
+ JavaType(const JavaType& other);
+ ~JavaType();
+ JavaType& operator=(const JavaType& other);
+
+ // Java's reflection API represents types as a string using an extended
+ // 'binary name'.
+ static JavaType CreateFromBinaryName(const std::string& binary_name);
+
+ enum Type {
+ TypeBoolean,
+ TypeByte,
+ TypeChar,
+ TypeShort,
+ TypeInt,
+ TypeLong,
+ TypeFloat,
+ TypeDouble,
+ // This is only used as a return type, so we should never convert from
+ // JavaScript with this type.
+ TypeVoid,
+ TypeArray,
+ // We special-case strings, as they get special handling when coercing.
+ TypeString,
+ TypeObject,
+ };
+
+ Type type;
+ scoped_ptr<JavaType> inner_type; // Used for TypeArray only.
+};
+
+} // namespace content
+
+#endif // CONTENT_BROWSER_RENDERER_HOST_JAVA_JAVA_TYPE_H_