OLD | NEW |
(Empty) | |
| 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 |
OLD | NEW |