// 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/scoped_java_ref.h" #include "content/browser/android/java/jni_reflect.h" #include "jni/Object_jni.h" using base::android::AttachCurrentThread; using base::android::ScopedJavaLocalRef; namespace content { // static GinJavaBoundObject* GinJavaBoundObject::CreateNamed( const JavaObjectWeakGlobalRef& ref, const base::android::JavaRef& safe_annotation_clazz) { return new GinJavaBoundObject(ref, safe_annotation_clazz); } // static GinJavaBoundObject* GinJavaBoundObject::CreateTransient( const JavaObjectWeakGlobalRef& ref, const base::android::JavaRef& safe_annotation_clazz, int32_t holder) { std::set holders; holders.insert(holder); return new GinJavaBoundObject(ref, safe_annotation_clazz, holders); } GinJavaBoundObject::GinJavaBoundObject( const JavaObjectWeakGlobalRef& ref, const base::android::JavaRef& safe_annotation_clazz) : ref_(ref), names_count_(1), are_methods_set_up_(false), safe_annotation_clazz_(safe_annotation_clazz) { } GinJavaBoundObject::GinJavaBoundObject( const JavaObjectWeakGlobalRef& ref, const base::android::JavaRef& safe_annotation_clazz, const std::set& holders) : ref_(ref), names_count_(0), holders_(holders), are_methods_set_up_(false), safe_annotation_clazz_(safe_annotation_clazz) {} GinJavaBoundObject::~GinJavaBoundObject() { } std::set GinJavaBoundObject::GetMethodNames() { EnsureMethodsAreSetUp(); std::set 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 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. JNIEnv* env = AttachCurrentThread(); jmethodID get_class_method_id = base::android::MethodID::LazyGet( env, java_lang_Object_clazz(env), "getClass", "()Ljava/lang/Class;", &JNI_Object::g_java_lang_Object_getClass); return method->id() == get_class_method_id; } const base::android::JavaRef& GinJavaBoundObject::GetSafeAnnotationClass() { return safe_annotation_clazz_; } base::android::ScopedJavaLocalRef GinJavaBoundObject::GetLocalClassRef( JNIEnv* env) { ScopedJavaLocalRef obj = GetLocalRef(env); if (obj.obj()) { return JNI_Object::Java_Object_getClass(env, obj); } else { return base::android::ScopedJavaLocalRef(); } } void GinJavaBoundObject::EnsureMethodsAreSetUp() { if (are_methods_set_up_) return; are_methods_set_up_ = true; JNIEnv* env = AttachCurrentThread(); ScopedJavaLocalRef clazz = GetLocalClassRef(env); if (clazz.is_null()) { return; } ScopedJavaLocalRef methods(GetClassMethods(env, clazz)); 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 java_method( env, env->GetObjectArrayElement(methods.obj(), i)); if (!safe_annotation_clazz_.is_null()) { if (!IsAnnotationPresent(env, java_method, safe_annotation_clazz_)) continue; } std::unique_ptr method(new JavaMethod(java_method)); methods_.insert(std::make_pair(method->name(), std::move(method))); } } } // namespace content