Upstream version 9.37.197.0
[platform/framework/web/crosswalk.git] / src / content / browser / renderer_host / java / gin_java_bound_object.cc
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