| 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 |