blob: 706f81e02d8b2e5984a14ae2e2101cdbcb026c5f [file] [log] [blame]
// 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/android/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/android/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,
int32_t holder) {
std::set<int32_t> 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<int32_t>& 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;
}
std::unique_ptr<JavaMethod> method(new JavaMethod(java_method));
methods_.insert(std::make_pair(method->name(), std::move(method)));
}
}
} // namespace content