1 /*
2 * Copyright 2010, The Android Open Source Project
3 *
4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions
6 * are met:
7 * * Redistributions of source code must retain the above copyright
8 * notice, this list of conditions and the following disclaimer.
9 * * Redistributions in binary form must reproduce the above copyright
10 * notice, this list of conditions and the following disclaimer in the
11 * documentation and/or other materials provided with the distribution.
12 *
13 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY
14 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
15 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
16 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
17 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
18 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
19 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
20 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
21 * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
22 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
23 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
24 */
25
26 #include "config.h"
27 #include "JavaClassJobjectV8.h"
28
29 #if ENABLE(JAVA_BRIDGE)
30
31 #include "JavaFieldJobjectV8.h"
32 #include "JavaMethodJobject.h"
33
34 using namespace JSC::Bindings;
35
36 #if PLATFORM(ANDROID)
37 const char kJavaScriptInterfaceAnnotation[] = "android/webkit/JavascriptInterface";
38 const char kIsAnnotationPresent[] = "isAnnotationPresent";
39 const char kGetMethods[] = "getMethods";
40
41 static jclass safeAnnotationClazz = NULL;
42
JavaClassJobject(jobject anInstance,bool requireAnnotation)43 JavaClassJobject::JavaClassJobject(jobject anInstance, bool requireAnnotation)
44 : m_requireAnnotation(requireAnnotation)
45 #else
46 JavaClassJobject::JavaClassJobject(jobject anInstance)
47 #endif
48 {
49 jobject aClass = callJNIMethod<jobject>(anInstance, "getClass", "()Ljava/lang/Class;");
50
51 if (!aClass) {
52 LOG_ERROR("unable to call getClass on instance %p", anInstance);
53 return;
54 }
55
56 JNIEnv* env = getJNIEnv();
57
58 // Get the fields
59 jarray fields = static_cast<jarray>(callJNIMethod<jobject>(aClass, "getFields", "()[Ljava/lang/reflect/Field;"));
60 int numFields = env->GetArrayLength(fields);
61 for (int i = 0; i < numFields; i++) {
62 jobject aJField = env->GetObjectArrayElement(static_cast<jobjectArray>(fields), i);
63 JavaField* aField = new JavaFieldJobject(env, aJField); // deleted in the JavaClass destructor
64 m_fields.set(aField->name(), aField);
65 env->DeleteLocalRef(aJField);
66 }
67
68 // Get the methods
69 jarray methods = static_cast<jarray>(callJNIMethod<jobject>(aClass, "getMethods", "()[Ljava/lang/reflect/Method;"));
70 int numMethods = env->GetArrayLength(methods);
71 #if PLATFORM(ANDROID)
72 jmethodID isAnnotationPresentMethodID = getAnnotationMethodID(env);
73 if (!isAnnotationPresentMethodID) {
74 LOG_ERROR("unable to find method %s on instance %p", kIsAnnotationPresent, anInstance);
75 return;
76 }
77 #endif
78 for (int i = 0; i < numMethods; i++) {
79 jobject aJMethod = env->GetObjectArrayElement(static_cast<jobjectArray>(methods), i);
80 #if PLATFORM(ANDROID)
81 if (jsAccessAllowed(env, isAnnotationPresentMethodID, aJMethod)) {
82 #endif
83 JavaMethod* aMethod = new JavaMethodJobject(env, aJMethod); // deleted in the JavaClass destructor
84 MethodList* methodList = m_methods.get(aMethod->name());
85 if (!methodList) {
86 methodList = new MethodList();
87 m_methods.set(aMethod->name(), methodList);
88 }
89 methodList->append(aMethod);
90 #if PLATFORM(ANDROID)
91 }
92 #endif
93 env->DeleteLocalRef(aJMethod);
94 }
95 env->DeleteLocalRef(fields);
96 env->DeleteLocalRef(methods);
97 env->DeleteLocalRef(aClass);
98 }
99
~JavaClassJobject()100 JavaClassJobject::~JavaClassJobject()
101 {
102 deleteAllValues(m_fields);
103 m_fields.clear();
104
105 MethodListMap::const_iterator end = m_methods.end();
106 for (MethodListMap::const_iterator it = m_methods.begin(); it != end; ++it) {
107 const MethodList* methodList = it->second;
108 deleteAllValues(*methodList);
109 delete methodList;
110 }
111 m_methods.clear();
112 }
113
114 #if PLATFORM(ANDROID)
jsAccessAllowed(JNIEnv * env,jmethodID mid,jobject aJMethod)115 bool JavaClassJobject::jsAccessAllowed(JNIEnv* env, jmethodID mid, jobject aJMethod)
116 {
117 if (!m_requireAnnotation)
118 return true;
119 bool accessAllowed = env->CallBooleanMethod(aJMethod, mid, safeAnnotationClazz);
120 if (env->ExceptionCheck()) {
121 env->ExceptionDescribe();
122 env->ExceptionClear();
123 return false;
124 }
125 return accessAllowed;
126 }
127
getAnnotationMethodID(JNIEnv * env)128 jmethodID JavaClassJobject::getAnnotationMethodID(JNIEnv* env)
129 {
130 jclass methodClass = env->FindClass("java/lang/reflect/Method");
131 jmethodID mid = 0;
132 if (methodClass)
133 mid = env->GetMethodID(methodClass, kIsAnnotationPresent, "(Ljava/lang/Class;)Z");
134 if (!methodClass || !mid) {
135 env->ExceptionDescribe();
136 env->ExceptionClear();
137 }
138 env->DeleteLocalRef(methodClass);
139 return mid;
140 }
141
RegisterJavaClassJobject(JNIEnv * env)142 bool JavaClassJobject::RegisterJavaClassJobject(JNIEnv* env) {
143 safeAnnotationClazz = reinterpret_cast<jclass>(env->NewGlobalRef(env->FindClass(kJavaScriptInterfaceAnnotation)));
144 if (!safeAnnotationClazz) {
145 LOG_ERROR("failed to register %s", kJavaScriptInterfaceAnnotation);
146 return false;
147 }
148 return true;
149 }
150 #endif
151
methodsNamed(const char * name) const152 MethodList JavaClassJobject::methodsNamed(const char* name) const
153 {
154 MethodList* methodList = m_methods.get(name);
155
156 if (methodList)
157 return *methodList;
158 return MethodList();
159 }
160
fieldNamed(const char * name) const161 JavaField* JavaClassJobject::fieldNamed(const char* name) const
162 {
163 return m_fields.get(name);
164 }
165
166 #endif // ENABLE(JAVA_BRIDGE)
167