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/android/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/android/java/jni_helper.h"
12
13using base::android::AttachCurrentThread;
14using base::android::ScopedJavaLocalRef;
15
16namespace content {
17
18namespace {
19
20const char kJavaLangClass[] = "java/lang/Class";
21const char kJavaLangObject[] = "java/lang/Object";
22const char kJavaLangReflectMethod[] = "java/lang/reflect/Method";
23const char kGetClass[] = "getClass";
24const char kGetMethods[] = "getMethods";
25const char kIsAnnotationPresent[] = "isAnnotationPresent";
26const char kReturningJavaLangClass[] = "()Ljava/lang/Class;";
27const char kReturningJavaLangReflectMethodArray[] =
28    "()[Ljava/lang/reflect/Method;";
29const char kTakesJavaLangClassReturningBoolean[] = "(Ljava/lang/Class;)Z";
30
31}  // namespace
32
33
34// static
35GinJavaBoundObject* 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
42GinJavaBoundObject* 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);
49}
50
51GinJavaBoundObject::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
61GinJavaBoundObject::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) {
71}
72
73GinJavaBoundObject::~GinJavaBoundObject() {
74}
75
76std::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
87bool GinJavaBoundObject::HasMethod(const std::string& method_name) {
88  EnsureMethodsAreSetUp();
89  return methods_.find(method_name) != methods_.end();
90}
91
92const 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
117bool 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
124const base::android::JavaRef<jclass>&
125GinJavaBoundObject::GetSafeAnnotationClass() {
126  return safe_annotation_clazz_;
127}
128
129base::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
146void 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
193