Re-subimission of https://codereview.chromium.org/1041213003/
[chromium-blink-merge.git] / content / browser / android / java / gin_java_bound_object.cc
blob527fcef83bda5aca943d22e971c337ed729aec8c
1 // Copyright 2014 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
5 #include "content/browser/android/java/gin_java_bound_object.h"
7 #include "base/android/jni_android.h"
8 #include "base/android/jni_string.h"
9 #include "base/android/scoped_java_ref.h"
10 #include "base/strings/utf_string_conversions.h"
11 #include "content/browser/android/java/jni_helper.h"
13 using base::android::AttachCurrentThread;
14 using base::android::ScopedJavaLocalRef;
16 namespace content {
18 namespace {
20 const char kJavaLangClass[] = "java/lang/Class";
21 const char kJavaLangObject[] = "java/lang/Object";
22 const char kJavaLangReflectMethod[] = "java/lang/reflect/Method";
23 const char kGetClass[] = "getClass";
24 const char kGetMethods[] = "getMethods";
25 const char kIsAnnotationPresent[] = "isAnnotationPresent";
26 const char kReturningJavaLangClass[] = "()Ljava/lang/Class;";
27 const char kReturningJavaLangReflectMethodArray[] =
28 "()[Ljava/lang/reflect/Method;";
29 const char kTakesJavaLangClassReturningBoolean[] = "(Ljava/lang/Class;)Z";
31 } // namespace
34 // static
35 GinJavaBoundObject* GinJavaBoundObject::CreateNamed(
36 const JavaObjectWeakGlobalRef& ref,
37 const base::android::JavaRef<jclass>& safe_annotation_clazz) {
38 return new GinJavaBoundObject(ref, safe_annotation_clazz);
41 // static
42 GinJavaBoundObject* GinJavaBoundObject::CreateTransient(
43 const JavaObjectWeakGlobalRef& ref,
44 const base::android::JavaRef<jclass>& safe_annotation_clazz,
45 int32 holder) {
46 std::set<int32> holders;
47 holders.insert(holder);
48 return new GinJavaBoundObject(ref, safe_annotation_clazz, holders);
51 GinJavaBoundObject::GinJavaBoundObject(
52 const JavaObjectWeakGlobalRef& ref,
53 const base::android::JavaRef<jclass>& safe_annotation_clazz)
54 : ref_(ref),
55 names_count_(1),
56 object_get_class_method_id_(NULL),
57 are_methods_set_up_(false),
58 safe_annotation_clazz_(safe_annotation_clazz) {
61 GinJavaBoundObject::GinJavaBoundObject(
62 const JavaObjectWeakGlobalRef& ref,
63 const base::android::JavaRef<jclass>& safe_annotation_clazz,
64 const std::set<int32>& holders)
65 : ref_(ref),
66 names_count_(0),
67 holders_(holders),
68 object_get_class_method_id_(NULL),
69 are_methods_set_up_(false),
70 safe_annotation_clazz_(safe_annotation_clazz) {
73 GinJavaBoundObject::~GinJavaBoundObject() {
76 std::set<std::string> GinJavaBoundObject::GetMethodNames() {
77 EnsureMethodsAreSetUp();
78 std::set<std::string> result;
79 for (JavaMethodMap::const_iterator it = methods_.begin();
80 it != methods_.end();
81 ++it) {
82 result.insert(it->first);
84 return result;
87 bool GinJavaBoundObject::HasMethod(const std::string& method_name) {
88 EnsureMethodsAreSetUp();
89 return methods_.find(method_name) != methods_.end();
92 const JavaMethod* GinJavaBoundObject::FindMethod(
93 const std::string& method_name,
94 size_t num_parameters) {
95 EnsureMethodsAreSetUp();
97 // Get all methods with the correct name.
98 std::pair<JavaMethodMap::const_iterator, JavaMethodMap::const_iterator>
99 iters = methods_.equal_range(method_name);
100 if (iters.first == iters.second) {
101 return NULL;
104 // LIVECONNECT_COMPLIANCE: We just take the first method with the correct
105 // number of arguments, while the spec proposes using cost-based algorithm:
106 // https://jdk6.java.net/plugin2/liveconnect/#OVERLOADED_METHODS
107 for (JavaMethodMap::const_iterator iter = iters.first; iter != iters.second;
108 ++iter) {
109 if (iter->second->num_parameters() == num_parameters) {
110 return iter->second.get();
114 return NULL;
117 bool GinJavaBoundObject::IsObjectGetClassMethod(const JavaMethod* method) {
118 EnsureMethodsAreSetUp();
119 // As java.lang.Object.getClass is declared to be final, it is sufficient to
120 // compare methodIDs.
121 return method->id() == object_get_class_method_id_;
124 const base::android::JavaRef<jclass>&
125 GinJavaBoundObject::GetSafeAnnotationClass() {
126 return safe_annotation_clazz_;
129 base::android::ScopedJavaLocalRef<jclass> GinJavaBoundObject::GetLocalClassRef(
130 JNIEnv* env) {
131 if (!object_get_class_method_id_) {
132 object_get_class_method_id_ = GetMethodIDFromClassName(
133 env, kJavaLangObject, kGetClass, kReturningJavaLangClass);
135 ScopedJavaLocalRef<jobject> obj = GetLocalRef(env);
136 if (obj.obj()) {
137 return base::android::ScopedJavaLocalRef<jclass>(
138 env,
139 static_cast<jclass>(
140 env->CallObjectMethod(obj.obj(), object_get_class_method_id_)));
141 } else {
142 return base::android::ScopedJavaLocalRef<jclass>();
146 void GinJavaBoundObject::EnsureMethodsAreSetUp() {
147 if (are_methods_set_up_)
148 return;
149 are_methods_set_up_ = true;
151 JNIEnv* env = AttachCurrentThread();
153 ScopedJavaLocalRef<jclass> clazz = GetLocalClassRef(env);
154 if (clazz.is_null()) {
155 return;
158 ScopedJavaLocalRef<jobjectArray> methods(env, static_cast<jobjectArray>(
159 env->CallObjectMethod(clazz.obj(), GetMethodIDFromClassName(
160 env,
161 kJavaLangClass,
162 kGetMethods,
163 kReturningJavaLangReflectMethodArray))));
165 size_t num_methods = env->GetArrayLength(methods.obj());
166 // Java objects always have public methods.
167 DCHECK(num_methods);
169 for (size_t i = 0; i < num_methods; ++i) {
170 ScopedJavaLocalRef<jobject> java_method(
171 env,
172 env->GetObjectArrayElement(methods.obj(), i));
174 if (!safe_annotation_clazz_.is_null()) {
175 jboolean safe = env->CallBooleanMethod(java_method.obj(),
176 GetMethodIDFromClassName(
177 env,
178 kJavaLangReflectMethod,
179 kIsAnnotationPresent,
180 kTakesJavaLangClassReturningBoolean),
181 safe_annotation_clazz_.obj());
183 if (!safe)
184 continue;
187 JavaMethod* method = new JavaMethod(java_method);
188 methods_.insert(std::make_pair(method->name(), make_linked_ptr(method)));
192 } // namespace content