Home | History | Annotate | Download | only in java
      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 
     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     int32 holder) {
     46   std::set<int32> 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<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 
     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
    193