OLD | NEW |
| (Empty) |
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. | |
4 | |
5 #include "content/browser/renderer_host/java/gin_java_bound_object.h" | |
6 | |
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/renderer_host/java/jni_helper.h" | |
12 | |
13 using base::android::AttachCurrentThread; | |
14 using base::android::ScopedJavaLocalRef; | |
15 | |
16 namespace content { | |
17 | |
18 namespace { | |
19 | |
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"; | |
30 | |
31 } // namespace | |
32 | |
33 | |
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); | |
39 } | |
40 | |
41 // static | |
42 GinJavaBoundObject* GinJavaBoundObject::CreateTransient( | |
43 const JavaObjectWeakGlobalRef& ref, | |
44 const base::android::JavaRef<jclass>& safe_annotation_clazz, | |
45 RenderFrameHost* holder) { | |
46 std::set<RenderFrameHost*> holders; | |
47 holders.insert(holder); | |
48 return new GinJavaBoundObject(ref, safe_annotation_clazz, holders); | |
49 } | |
50 | |
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) { | |
59 } | |
60 | |
61 GinJavaBoundObject::GinJavaBoundObject( | |
62 const JavaObjectWeakGlobalRef& ref, | |
63 const base::android::JavaRef<jclass>& safe_annotation_clazz, | |
64 const std::set<RenderFrameHost*> 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) { | |
71 } | |
72 | |
73 GinJavaBoundObject::~GinJavaBoundObject() { | |
74 } | |
75 | |
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); | |
83 } | |
84 return result; | |
85 } | |
86 | |
87 bool GinJavaBoundObject::HasMethod(const std::string& method_name) { | |
88 EnsureMethodsAreSetUp(); | |
89 return methods_.find(method_name) != methods_.end(); | |
90 } | |
91 | |
92 const JavaMethod* GinJavaBoundObject::FindMethod( | |
93 const std::string& method_name, | |
94 size_t num_parameters) { | |
95 EnsureMethodsAreSetUp(); | |
96 | |
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; | |
102 } | |
103 | |
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(); | |
111 } | |
112 } | |
113 | |
114 return NULL; | |
115 } | |
116 | |
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_; | |
122 } | |
123 | |
124 const base::android::JavaRef<jclass>& | |
125 GinJavaBoundObject::GetSafeAnnotationClass() { | |
126 return safe_annotation_clazz_; | |
127 } | |
128 | |
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); | |
134 } | |
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>(); | |
143 } | |
144 } | |
145 | |
146 void GinJavaBoundObject::EnsureMethodsAreSetUp() { | |
147 if (are_methods_set_up_) | |
148 return; | |
149 are_methods_set_up_ = true; | |
150 | |
151 JNIEnv* env = AttachCurrentThread(); | |
152 | |
153 ScopedJavaLocalRef<jclass> clazz = GetLocalClassRef(env); | |
154 if (clazz.is_null()) { | |
155 return; | |
156 } | |
157 | |
158 ScopedJavaLocalRef<jobjectArray> methods(env, static_cast<jobjectArray>( | |
159 env->CallObjectMethod(clazz.obj(), GetMethodIDFromClassName( | |
160 env, | |
161 kJavaLangClass, | |
162 kGetMethods, | |
163 kReturningJavaLangReflectMethodArray)))); | |
164 | |
165 size_t num_methods = env->GetArrayLength(methods.obj()); | |
166 // Java objects always have public methods. | |
167 DCHECK(num_methods); | |
168 | |
169 for (size_t i = 0; i < num_methods; ++i) { | |
170 ScopedJavaLocalRef<jobject> java_method( | |
171 env, | |
172 env->GetObjectArrayElement(methods.obj(), i)); | |
173 | |
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()); | |
182 | |
183 if (!safe) | |
184 continue; | |
185 } | |
186 | |
187 JavaMethod* method = new JavaMethod(java_method); | |
188 methods_.insert(std::make_pair(method->name(), method)); | |
189 } | |
190 } | |
191 | |
192 } // namespace content | |
OLD | NEW |