OLD | NEW |
1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. | 1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. |
2 // Use of this source code is governed by a BSD-style license that can be | 2 // Use of this source code is governed by a BSD-style license that can be |
3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
4 | 4 |
5 #include "content/browser/renderer_host/java/java_bound_object.h" | 5 #include "content/browser/renderer_host/java/java_bound_object.h" |
6 | 6 |
7 #include "base/android/jni_android.h" | 7 #include "base/android/jni_android.h" |
8 #include "base/android/jni_string.h" | 8 #include "base/android/jni_string.h" |
9 #include "base/memory/singleton.h" | 9 #include "base/memory/singleton.h" |
10 #include "base/string_number_conversions.h" | 10 #include "base/string_number_conversions.h" |
(...skipping 17 matching lines...) Expand all Loading... |
28 | 28 |
29 // Note that in some cases, we differ from from the spec in order to maintain | 29 // Note that in some cases, we differ from from the spec in order to maintain |
30 // existing behavior. These areas are marked LIVECONNECT_COMPLIANCE. We may | 30 // existing behavior. These areas are marked LIVECONNECT_COMPLIANCE. We may |
31 // revisit this decision in the future. | 31 // revisit this decision in the future. |
32 | 32 |
33 namespace { | 33 namespace { |
34 | 34 |
35 const char kJavaLangClass[] = "java/lang/Class"; | 35 const char kJavaLangClass[] = "java/lang/Class"; |
36 const char kJavaLangObject[] = "java/lang/Object"; | 36 const char kJavaLangObject[] = "java/lang/Object"; |
37 const char kJavaLangReflectMethod[] = "java/lang/reflect/Method"; | 37 const char kJavaLangReflectMethod[] = "java/lang/reflect/Method"; |
| 38 // TODO(dtrainor): Parameterize this so that WebView and Chrome for Android can |
| 39 // use different annotations. |
| 40 const char kJavaScriptInterfaceAnnotation[] = |
| 41 "org/chromium/content/browser/JavascriptInterface"; |
38 const char kGetClass[] = "getClass"; | 42 const char kGetClass[] = "getClass"; |
39 const char kGetDeclaredMethods[] = "getDeclaredMethods"; | |
40 const char kGetMethods[] = "getMethods"; | 43 const char kGetMethods[] = "getMethods"; |
41 const char kGetModifiers[] = "getModifiers"; | 44 const char kIsAnnotationPresent[] = "isAnnotationPresent"; |
42 const char kReturningInteger[] = "()I"; | |
43 const char kReturningJavaLangClass[] = "()Ljava/lang/Class;"; | 45 const char kReturningJavaLangClass[] = "()Ljava/lang/Class;"; |
44 const char kReturningJavaLangReflectMethodArray[] = | 46 const char kReturningJavaLangReflectMethodArray[] = |
45 "()[Ljava/lang/reflect/Method;"; | 47 "()[Ljava/lang/reflect/Method;"; |
| 48 const char kTakesJavaLangClassReturningBoolean[] = "(Ljava/lang/Class;)Z"; |
46 | 49 |
47 // This constant represents the value at java.lang.reflect.Modifier.PUBLIC. | 50 jclass g_safe_annotation_clazz = NULL; |
48 const int kJavaPublicModifier = 1; | |
49 | 51 |
50 // Our special NPObject type. We extend an NPObject with a pointer to a | 52 // Our special NPObject type. We extend an NPObject with a pointer to a |
51 // JavaBoundObject. We also add static methods for each of the NPObject | 53 // JavaBoundObject. We also add static methods for each of the NPObject |
52 // callbacks, which are registered by our NPClass. These methods simply | 54 // callbacks, which are registered by our NPClass. These methods simply |
53 // delegate to the private implementation methods of JavaBoundObject. | 55 // delegate to the private implementation methods of JavaBoundObject. |
54 struct JavaNPObject : public NPObject { | 56 struct JavaNPObject : public NPObject { |
55 JavaBoundObject* bound_object; | 57 JavaBoundObject* bound_object; |
56 | 58 |
57 static const NPClass kNPClass; | 59 static const NPClass kNPClass; |
58 | 60 |
(...skipping 62 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
121 return false; | 123 return false; |
122 } | 124 } |
123 | 125 |
124 // Calls a Java method through JNI. If the Java method raises an uncaught | 126 // Calls a Java method through JNI. If the Java method raises an uncaught |
125 // exception, it is cleared and this method returns false. Otherwise, this | 127 // exception, it is cleared and this method returns false. Otherwise, this |
126 // method returns true and the Java method's return value is provided as an | 128 // method returns true and the Java method's return value is provided as an |
127 // NPVariant. Note that this method does not do any type coercion. The Java | 129 // NPVariant. Note that this method does not do any type coercion. The Java |
128 // return value is simply converted to the corresponding NPAPI type. | 130 // return value is simply converted to the corresponding NPAPI type. |
129 bool CallJNIMethod(jobject object, const JavaType& return_type, jmethodID id, | 131 bool CallJNIMethod(jobject object, const JavaType& return_type, jmethodID id, |
130 jvalue* parameters, NPVariant* result, | 132 jvalue* parameters, NPVariant* result, |
131 bool allow_inherited_methods) { | 133 bool require_annotation) { |
132 JNIEnv* env = AttachCurrentThread(); | 134 JNIEnv* env = AttachCurrentThread(); |
133 switch (return_type.type) { | 135 switch (return_type.type) { |
134 case JavaType::TypeBoolean: | 136 case JavaType::TypeBoolean: |
135 BOOLEAN_TO_NPVARIANT(env->CallBooleanMethodA(object, id, parameters), | 137 BOOLEAN_TO_NPVARIANT(env->CallBooleanMethodA(object, id, parameters), |
136 *result); | 138 *result); |
137 break; | 139 break; |
138 case JavaType::TypeByte: | 140 case JavaType::TypeByte: |
139 INT32_TO_NPVARIANT(env->CallByteMethodA(object, id, parameters), *result); | 141 INT32_TO_NPVARIANT(env->CallByteMethodA(object, id, parameters), *result); |
140 break; | 142 break; |
141 case JavaType::TypeChar: | 143 case JavaType::TypeChar: |
(...skipping 61 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
203 jobject java_object = env->CallObjectMethodA(object, id, parameters); | 205 jobject java_object = env->CallObjectMethodA(object, id, parameters); |
204 if (base::android::ClearException(env)) { | 206 if (base::android::ClearException(env)) { |
205 return false; | 207 return false; |
206 } | 208 } |
207 ScopedJavaLocalRef<jobject> scoped_java_object(env, java_object); | 209 ScopedJavaLocalRef<jobject> scoped_java_object(env, java_object); |
208 if (!scoped_java_object.obj()) { | 210 if (!scoped_java_object.obj()) { |
209 NULL_TO_NPVARIANT(*result); | 211 NULL_TO_NPVARIANT(*result); |
210 break; | 212 break; |
211 } | 213 } |
212 OBJECT_TO_NPVARIANT(JavaBoundObject::Create(scoped_java_object, | 214 OBJECT_TO_NPVARIANT(JavaBoundObject::Create(scoped_java_object, |
213 allow_inherited_methods), | 215 require_annotation), |
214 *result); | 216 *result); |
215 break; | 217 break; |
216 } | 218 } |
217 } | 219 } |
218 return !base::android::ClearException(env); | 220 return !base::android::ClearException(env); |
219 } | 221 } |
220 | 222 |
221 jvalue CoerceJavaScriptNumberToJavaValue(const NPVariant& variant, | 223 jvalue CoerceJavaScriptNumberToJavaValue(const NPVariant& variant, |
222 const JavaType& target_type, | 224 const JavaType& target_type, |
223 bool coerce_to_string) { | 225 bool coerce_to_string) { |
(...skipping 495 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
719 case NPVariantType_Void: | 721 case NPVariantType_Void: |
720 return CoerceJavaScriptNullOrUndefinedToJavaValue(variant, target_type, | 722 return CoerceJavaScriptNullOrUndefinedToJavaValue(variant, target_type, |
721 coerce_to_string); | 723 coerce_to_string); |
722 } | 724 } |
723 NOTREACHED(); | 725 NOTREACHED(); |
724 return jvalue(); | 726 return jvalue(); |
725 } | 727 } |
726 | 728 |
727 } // namespace | 729 } // namespace |
728 | 730 |
729 | |
730 NPObject* JavaBoundObject::Create(const JavaRef<jobject>& object, | 731 NPObject* JavaBoundObject::Create(const JavaRef<jobject>& object, |
731 bool allow_inherited_methods) { | 732 bool require_annotation) { |
732 // The first argument (a plugin's instance handle) is passed through to the | 733 // The first argument (a plugin's instance handle) is passed through to the |
733 // allocate function directly, and we don't use it, so it's ok to be 0. | 734 // allocate function directly, and we don't use it, so it's ok to be 0. |
734 // The object is created with a ref count of one. | 735 // The object is created with a ref count of one. |
735 NPObject* np_object = WebBindings::createObject(0, const_cast<NPClass*>( | 736 NPObject* np_object = WebBindings::createObject(0, const_cast<NPClass*>( |
736 &JavaNPObject::kNPClass)); | 737 &JavaNPObject::kNPClass)); |
737 // The NPObject takes ownership of the JavaBoundObject. | 738 // The NPObject takes ownership of the JavaBoundObject. |
738 reinterpret_cast<JavaNPObject*>(np_object)->bound_object = | 739 reinterpret_cast<JavaNPObject*>(np_object)->bound_object = |
739 new JavaBoundObject(object, allow_inherited_methods); | 740 new JavaBoundObject(object, require_annotation); |
740 return np_object; | 741 return np_object; |
741 } | 742 } |
742 | 743 |
743 JavaBoundObject::JavaBoundObject(const JavaRef<jobject>& object, | 744 JavaBoundObject::JavaBoundObject(const JavaRef<jobject>& object, |
744 bool allow_inherited_methods) | 745 bool require_annotation) |
745 : java_object_(object), | 746 : java_object_(object), |
746 are_methods_set_up_(false), | 747 are_methods_set_up_(false), |
747 allow_inherited_methods_(allow_inherited_methods) { | 748 require_annotation_(require_annotation) { |
748 // We don't do anything with our Java object when first created. We do it all | 749 // We don't do anything with our Java object when first created. We do it all |
749 // lazily when a method is first invoked. | 750 // lazily when a method is first invoked. |
750 } | 751 } |
751 | 752 |
752 JavaBoundObject::~JavaBoundObject() { | 753 JavaBoundObject::~JavaBoundObject() { |
753 } | 754 } |
754 | 755 |
755 jobject JavaBoundObject::GetJavaObject(NPObject* object) { | 756 jobject JavaBoundObject::GetJavaObject(NPObject* object) { |
756 DCHECK_EQ(&JavaNPObject::kNPClass, object->_class); | 757 DCHECK_EQ(&JavaNPObject::kNPClass, object->_class); |
757 JavaBoundObject* jbo = reinterpret_cast<JavaNPObject*>(object)->bound_object; | 758 JavaBoundObject* jbo = reinterpret_cast<JavaNPObject*>(object)->bound_object; |
(...skipping 33 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
791 std::vector<jvalue> parameters(arg_count); | 792 std::vector<jvalue> parameters(arg_count); |
792 for (size_t i = 0; i < arg_count; ++i) { | 793 for (size_t i = 0; i < arg_count; ++i) { |
793 parameters[i] = CoerceJavaScriptValueToJavaValue(args[i], | 794 parameters[i] = CoerceJavaScriptValueToJavaValue(args[i], |
794 method->parameter_type(i), | 795 method->parameter_type(i), |
795 true); | 796 true); |
796 } | 797 } |
797 | 798 |
798 // Call | 799 // Call |
799 bool ok = CallJNIMethod(java_object_.obj(), method->return_type(), | 800 bool ok = CallJNIMethod(java_object_.obj(), method->return_type(), |
800 method->id(), ¶meters[0], result, | 801 method->id(), ¶meters[0], result, |
801 allow_inherited_methods_); | 802 require_annotation_); |
802 | 803 |
803 // Now that we're done with the jvalue, release any local references created | 804 // Now that we're done with the jvalue, release any local references created |
804 // by CoerceJavaScriptValueToJavaValue(). | 805 // by CoerceJavaScriptValueToJavaValue(). |
805 JNIEnv* env = AttachCurrentThread(); | 806 JNIEnv* env = AttachCurrentThread(); |
806 for (size_t i = 0; i < arg_count; ++i) { | 807 for (size_t i = 0; i < arg_count; ++i) { |
807 ReleaseJavaValueIfRequired(env, ¶meters[i], method->parameter_type(i)); | 808 ReleaseJavaValueIfRequired(env, ¶meters[i], method->parameter_type(i)); |
808 } | 809 } |
809 | 810 |
810 return ok; | 811 return ok; |
811 } | 812 } |
812 | 813 |
| 814 bool JavaBoundObject::RegisterJavaBoundObject(JNIEnv* env) { |
| 815 g_safe_annotation_clazz = reinterpret_cast<jclass>(env->NewGlobalRef( |
| 816 base::android::GetUnscopedClass(env, kJavaScriptInterfaceAnnotation))); |
| 817 DCHECK(g_safe_annotation_clazz); |
| 818 |
| 819 return true; |
| 820 } |
| 821 |
813 void JavaBoundObject::EnsureMethodsAreSetUp() const { | 822 void JavaBoundObject::EnsureMethodsAreSetUp() const { |
814 if (are_methods_set_up_) | 823 if (are_methods_set_up_) |
815 return; | 824 return; |
816 are_methods_set_up_ = true; | 825 are_methods_set_up_ = true; |
817 | 826 |
818 JNIEnv* env = AttachCurrentThread(); | 827 JNIEnv* env = AttachCurrentThread(); |
819 ScopedJavaLocalRef<jclass> clazz(env, static_cast<jclass>( | 828 ScopedJavaLocalRef<jclass> clazz(env, static_cast<jclass>( |
820 env->CallObjectMethod(java_object_.obj(), GetMethodIDFromClassName( | 829 env->CallObjectMethod(java_object_.obj(), GetMethodIDFromClassName( |
821 env, | 830 env, |
822 kJavaLangObject, | 831 kJavaLangObject, |
823 kGetClass, | 832 kGetClass, |
824 kReturningJavaLangClass)))); | 833 kReturningJavaLangClass)))); |
825 | 834 |
826 const char* get_method = allow_inherited_methods_ ? | |
827 kGetMethods : kGetDeclaredMethods; | |
828 | |
829 ScopedJavaLocalRef<jobjectArray> methods(env, static_cast<jobjectArray>( | 835 ScopedJavaLocalRef<jobjectArray> methods(env, static_cast<jobjectArray>( |
830 env->CallObjectMethod(clazz.obj(), GetMethodIDFromClassName( | 836 env->CallObjectMethod(clazz.obj(), GetMethodIDFromClassName( |
831 env, | 837 env, |
832 kJavaLangClass, | 838 kJavaLangClass, |
833 get_method, | 839 kGetMethods, |
834 kReturningJavaLangReflectMethodArray)))); | 840 kReturningJavaLangReflectMethodArray)))); |
835 | 841 |
836 size_t num_methods = env->GetArrayLength(methods.obj()); | 842 size_t num_methods = env->GetArrayLength(methods.obj()); |
837 if (num_methods <= 0) | 843 // Java objects always have public methods. |
838 return; | 844 DCHECK(num_methods); |
839 | 845 |
840 for (size_t i = 0; i < num_methods; ++i) { | 846 for (size_t i = 0; i < num_methods; ++i) { |
841 ScopedJavaLocalRef<jobject> java_method( | 847 ScopedJavaLocalRef<jobject> java_method( |
842 env, | 848 env, |
843 env->GetObjectArrayElement(methods.obj(), i)); | 849 env->GetObjectArrayElement(methods.obj(), i)); |
844 | 850 |
845 bool is_method_allowed = true; | 851 if (require_annotation_) { |
846 if (!allow_inherited_methods_) { | 852 jboolean safe = env->CallBooleanMethod(java_method.obj(), |
847 jint modifiers = env->CallIntMethod(java_method.obj(), | 853 GetMethodIDFromClassName( |
848 GetMethodIDFromClassName( | 854 env, |
849 env, | 855 kJavaLangReflectMethod, |
850 kJavaLangReflectMethod, | 856 kIsAnnotationPresent, |
851 kGetModifiers, | 857 kTakesJavaLangClassReturningBoolean), |
852 kReturningInteger)); | 858 g_safe_annotation_clazz); |
853 is_method_allowed &= (modifiers & kJavaPublicModifier); | 859 |
| 860 if (!safe) |
| 861 continue; |
854 } | 862 } |
855 | 863 |
856 if (is_method_allowed) { | 864 JavaMethod* method = new JavaMethod(java_method); |
857 JavaMethod* method = new JavaMethod(java_method); | 865 methods_.insert(std::make_pair(method->name(), method)); |
858 methods_.insert(std::make_pair(method->name(), method)); | |
859 } | |
860 } | 866 } |
861 } | 867 } |
OLD | NEW |