Index: content/browser/renderer_host/java/gin_java_bound_object.cc |
diff --git a/content/browser/renderer_host/java/gin_java_bound_object.cc b/content/browser/renderer_host/java/gin_java_bound_object.cc |
new file mode 100644 |
index 0000000000000000000000000000000000000000..70abba842c90bbc2497cc50cec74512abc0a56de |
--- /dev/null |
+++ b/content/browser/renderer_host/java/gin_java_bound_object.cc |
@@ -0,0 +1,192 @@ |
+// Copyright 2014 The Chromium Authors. All rights reserved. |
+// Use of this source code is governed by a BSD-style license that can be |
+// found in the LICENSE file. |
+ |
+#include "content/browser/renderer_host/java/gin_java_bound_object.h" |
+ |
+#include "base/android/jni_android.h" |
+#include "base/android/jni_string.h" |
+#include "base/android/scoped_java_ref.h" |
+#include "base/strings/utf_string_conversions.h" |
+#include "content/browser/renderer_host/java/jni_helper.h" |
+ |
+using base::android::AttachCurrentThread; |
+using base::android::ScopedJavaLocalRef; |
+ |
+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"; |
+ |
+} // namespace |
+ |
+ |
+// static |
+GinJavaBoundObject* GinJavaBoundObject::CreateNamed( |
+ const JavaObjectWeakGlobalRef& ref, |
+ const base::android::JavaRef<jclass>& safe_annotation_clazz) { |
+ return new GinJavaBoundObject(ref, safe_annotation_clazz); |
+} |
+ |
+// static |
+GinJavaBoundObject* GinJavaBoundObject::CreateTransient( |
+ const JavaObjectWeakGlobalRef& ref, |
+ const base::android::JavaRef<jclass>& safe_annotation_clazz, |
+ RenderFrameHost* holder) { |
+ std::set<RenderFrameHost*> holders; |
+ holders.insert(holder); |
+ return new GinJavaBoundObject(ref, safe_annotation_clazz, holders); |
+} |
+ |
+GinJavaBoundObject::GinJavaBoundObject( |
+ const JavaObjectWeakGlobalRef& ref, |
+ const base::android::JavaRef<jclass>& safe_annotation_clazz) |
+ : ref_(ref), |
+ names_count_(1), |
+ object_get_class_method_id_(NULL), |
+ are_methods_set_up_(false), |
+ safe_annotation_clazz_(safe_annotation_clazz) { |
+} |
+ |
+GinJavaBoundObject::GinJavaBoundObject( |
+ const JavaObjectWeakGlobalRef& ref, |
+ const base::android::JavaRef<jclass>& safe_annotation_clazz, |
+ const std::set<RenderFrameHost*> holders) |
+ : ref_(ref), |
+ names_count_(0), |
+ holders_(holders), |
+ object_get_class_method_id_(NULL), |
+ are_methods_set_up_(false), |
+ safe_annotation_clazz_(safe_annotation_clazz) { |
+} |
+ |
+GinJavaBoundObject::~GinJavaBoundObject() { |
+} |
+ |
+std::set<std::string> GinJavaBoundObject::GetMethodNames() { |
+ EnsureMethodsAreSetUp(); |
+ std::set<std::string> result; |
+ for (JavaMethodMap::const_iterator it = methods_.begin(); |
+ it != methods_.end(); |
+ ++it) { |
+ result.insert(it->first); |
+ } |
+ return result; |
+} |
+ |
+bool GinJavaBoundObject::HasMethod(const std::string& method_name) { |
+ EnsureMethodsAreSetUp(); |
+ return methods_.find(method_name) != methods_.end(); |
+} |
+ |
+const JavaMethod* GinJavaBoundObject::FindMethod( |
+ const std::string& method_name, |
+ size_t num_parameters) { |
+ EnsureMethodsAreSetUp(); |
+ |
+ // Get all methods with the correct name. |
+ std::pair<JavaMethodMap::const_iterator, JavaMethodMap::const_iterator> |
+ iters = methods_.equal_range(method_name); |
+ if (iters.first == iters.second) { |
+ return NULL; |
+ } |
+ |
+ // LIVECONNECT_COMPLIANCE: We just take the first method with the correct |
+ // number of arguments, while the spec proposes using cost-based algorithm: |
+ // https://jdk6.java.net/plugin2/liveconnect/#OVERLOADED_METHODS |
+ for (JavaMethodMap::const_iterator iter = iters.first; iter != iters.second; |
+ ++iter) { |
+ if (iter->second->num_parameters() == num_parameters) { |
+ return iter->second.get(); |
+ } |
+ } |
+ |
+ return NULL; |
+} |
+ |
+bool GinJavaBoundObject::IsObjectGetClassMethod(const JavaMethod* method) { |
+ EnsureMethodsAreSetUp(); |
+ // As java.lang.Object.getClass is declared to be final, it is sufficient to |
+ // compare methodIDs. |
+ return method->id() == object_get_class_method_id_; |
+} |
+ |
+const base::android::JavaRef<jclass>& |
+GinJavaBoundObject::GetSafeAnnotationClass() { |
+ return safe_annotation_clazz_; |
+} |
+ |
+base::android::ScopedJavaLocalRef<jclass> GinJavaBoundObject::GetLocalClassRef( |
+ JNIEnv* env) { |
+ if (!object_get_class_method_id_) { |
+ object_get_class_method_id_ = GetMethodIDFromClassName( |
+ env, kJavaLangObject, kGetClass, kReturningJavaLangClass); |
+ } |
+ ScopedJavaLocalRef<jobject> obj = GetLocalRef(env); |
+ if (obj.obj()) { |
+ return base::android::ScopedJavaLocalRef<jclass>( |
+ env, |
+ static_cast<jclass>( |
+ env->CallObjectMethod(obj.obj(), object_get_class_method_id_))); |
+ } else { |
+ return base::android::ScopedJavaLocalRef<jclass>(); |
+ } |
+} |
+ |
+void GinJavaBoundObject::EnsureMethodsAreSetUp() { |
+ 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)); |
+ } |
+} |
+ |
+} // namespace content |