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_method_invocation_helper.h
" | |
6 | |
7 #include <unistd.h> | |
8 | |
9 #include "base/android/event_log.h" | |
10 #include "base/android/jni_android.h" | |
11 #include "base/android/jni_string.h" | |
12 #include "base/float_util.h" | |
13 #include "content/browser/renderer_host/java/gin_java_script_to_java_types_coerc
ion.h" | |
14 #include "content/browser/renderer_host/java/java_method.h" | |
15 #include "content/browser/renderer_host/java/jni_helper.h" | |
16 #include "content/common/android/gin_java_bridge_value.h" | |
17 #include "content/public/browser/browser_thread.h" | |
18 | |
19 using base::android::AttachCurrentThread; | |
20 using base::android::ScopedJavaLocalRef; | |
21 | |
22 namespace content { | |
23 | |
24 namespace { | |
25 | |
26 // See frameworks/base/core/java/android/webkit/EventLogTags.logtags | |
27 const int kObjectGetClassInvocationAttemptLogTag = 70151; | |
28 | |
29 } // namespace | |
30 | |
31 GinJavaMethodInvocationHelper::GinJavaMethodInvocationHelper( | |
32 scoped_ptr<ObjectDelegate> object, | |
33 const std::string& method_name, | |
34 const base::ListValue& arguments) | |
35 : object_(object.Pass()), | |
36 method_name_(method_name), | |
37 arguments_(arguments.DeepCopy()), | |
38 invocation_error_(kGinJavaBridgeNoError) { | |
39 } | |
40 | |
41 GinJavaMethodInvocationHelper::~GinJavaMethodInvocationHelper() {} | |
42 | |
43 void GinJavaMethodInvocationHelper::Init(DispatcherDelegate* dispatcher) { | |
44 // Build on the UI thread a map of object_id -> WeakRef for Java objects from | |
45 // |arguments_|. Then we can use this map on the background thread without | |
46 // accessing |dispatcher|. | |
47 BuildObjectRefsFromListValue(dispatcher, arguments_.get()); | |
48 } | |
49 | |
50 // As V8ValueConverter has finite recursion depth when serializing | |
51 // JavaScript values, we don't bother about having a recursion threshold here. | |
52 void GinJavaMethodInvocationHelper::BuildObjectRefsFromListValue( | |
53 DispatcherDelegate* dispatcher, | |
54 const base::Value* list_value) { | |
55 DCHECK(list_value->IsType(base::Value::TYPE_LIST)); | |
56 const base::ListValue* list; | |
57 list_value->GetAsList(&list); | |
58 for (base::ListValue::const_iterator iter = list->begin(); | |
59 iter != list->end(); | |
60 ++iter) { | |
61 if (AppendObjectRef(dispatcher, *iter)) | |
62 continue; | |
63 if ((*iter)->IsType(base::Value::TYPE_LIST)) { | |
64 BuildObjectRefsFromListValue(dispatcher, *iter); | |
65 } else if ((*iter)->IsType(base::Value::TYPE_DICTIONARY)) { | |
66 BuildObjectRefsFromDictionaryValue(dispatcher, *iter); | |
67 } | |
68 } | |
69 } | |
70 | |
71 void GinJavaMethodInvocationHelper::BuildObjectRefsFromDictionaryValue( | |
72 DispatcherDelegate* dispatcher, | |
73 const base::Value* dict_value) { | |
74 DCHECK(dict_value->IsType(base::Value::TYPE_DICTIONARY)); | |
75 const base::DictionaryValue* dict; | |
76 dict_value->GetAsDictionary(&dict); | |
77 for (base::DictionaryValue::Iterator iter(*dict); | |
78 !iter.IsAtEnd(); | |
79 iter.Advance()) { | |
80 if (AppendObjectRef(dispatcher, &iter.value())) | |
81 continue; | |
82 if (iter.value().IsType(base::Value::TYPE_LIST)) { | |
83 BuildObjectRefsFromListValue(dispatcher, &iter.value()); | |
84 } else if (iter.value().IsType(base::Value::TYPE_DICTIONARY)) { | |
85 BuildObjectRefsFromDictionaryValue(dispatcher, &iter.value()); | |
86 } | |
87 } | |
88 } | |
89 | |
90 bool GinJavaMethodInvocationHelper::AppendObjectRef( | |
91 DispatcherDelegate* dispatcher, | |
92 const base::Value* raw_value) { | |
93 if (!GinJavaBridgeValue::ContainsGinJavaBridgeValue(raw_value)) | |
94 return false; | |
95 scoped_ptr<const GinJavaBridgeValue> value( | |
96 GinJavaBridgeValue::FromValue(raw_value)); | |
97 if (!value->IsType(GinJavaBridgeValue::TYPE_OBJECT_ID)) | |
98 return false; | |
99 GinJavaBoundObject::ObjectID object_id; | |
100 if (value->GetAsObjectID(&object_id)) { | |
101 ObjectRefs::iterator iter = object_refs_.find(object_id); | |
102 if (iter == object_refs_.end()) { | |
103 JavaObjectWeakGlobalRef object_ref( | |
104 dispatcher->GetObjectWeakRef(object_id)); | |
105 if (!object_ref.is_empty()) { | |
106 object_refs_.insert(std::make_pair(object_id, object_ref)); | |
107 } | |
108 } | |
109 } | |
110 return true; | |
111 } | |
112 | |
113 void GinJavaMethodInvocationHelper::Invoke() { | |
114 JNIEnv* env = AttachCurrentThread(); | |
115 const JavaMethod* method = | |
116 object_->FindMethod(method_name_, arguments_->GetSize()); | |
117 if (!method) { | |
118 SetInvocationError(kGinJavaBridgeMethodNotFound); | |
119 return; | |
120 } | |
121 | |
122 if (object_->IsObjectGetClassMethod(method)) { | |
123 base::android::EventLogWriteInt(kObjectGetClassInvocationAttemptLogTag, | |
124 getuid()); | |
125 SetInvocationError(kGinJavaBridgeAccessToObjectGetClassIsBlocked); | |
126 return; | |
127 } | |
128 | |
129 ScopedJavaLocalRef<jobject> obj; | |
130 ScopedJavaLocalRef<jclass> cls; | |
131 if (method->is_static()) { | |
132 cls = object_->GetLocalClassRef(env); | |
133 } else { | |
134 obj = object_->GetLocalRef(env); | |
135 } | |
136 if (obj.is_null() && cls.is_null()) { | |
137 SetInvocationError(kGinJavaBridgeObjectIsGone); | |
138 return; | |
139 } | |
140 | |
141 std::vector<jvalue> parameters(method->num_parameters()); | |
142 for (size_t i = 0; i < method->num_parameters(); ++i) { | |
143 const base::Value* argument; | |
144 arguments_->Get(i, &argument); | |
145 parameters[i] = CoerceJavaScriptValueToJavaValue( | |
146 env, argument, method->parameter_type(i), true, object_refs_); | |
147 } | |
148 if (method->is_static()) { | |
149 InvokeMethod( | |
150 NULL, cls.obj(), method->return_type(), method->id(), ¶meters[0]); | |
151 } else { | |
152 InvokeMethod( | |
153 obj.obj(), NULL, method->return_type(), method->id(), ¶meters[0]); | |
154 } | |
155 | |
156 // Now that we're done with the jvalue, release any local references created | |
157 // by CoerceJavaScriptValueToJavaValue(). | |
158 for (size_t i = 0; i < method->num_parameters(); ++i) { | |
159 ReleaseJavaValueIfRequired(env, ¶meters[i], method->parameter_type(i)); | |
160 } | |
161 } | |
162 | |
163 void GinJavaMethodInvocationHelper::SetInvocationError( | |
164 GinJavaBridgeError error) { | |
165 holds_primitive_result_ = true; | |
166 primitive_result_.reset(new base::ListValue()); | |
167 invocation_error_ = error; | |
168 } | |
169 | |
170 void GinJavaMethodInvocationHelper::SetPrimitiveResult( | |
171 const base::ListValue& result_wrapper) { | |
172 holds_primitive_result_ = true; | |
173 primitive_result_.reset(result_wrapper.DeepCopy()); | |
174 } | |
175 | |
176 void GinJavaMethodInvocationHelper::SetObjectResult( | |
177 const base::android::JavaRef<jobject>& object, | |
178 const base::android::JavaRef<jclass>& safe_annotation_clazz) { | |
179 holds_primitive_result_ = false; | |
180 object_result_.Reset(object); | |
181 safe_annotation_clazz_.Reset(safe_annotation_clazz); | |
182 } | |
183 | |
184 bool GinJavaMethodInvocationHelper::HoldsPrimitiveResult() { | |
185 return holds_primitive_result_; | |
186 } | |
187 | |
188 const base::ListValue& GinJavaMethodInvocationHelper::GetPrimitiveResult() { | |
189 return *primitive_result_.get(); | |
190 } | |
191 | |
192 const base::android::JavaRef<jobject>& | |
193 GinJavaMethodInvocationHelper::GetObjectResult() { | |
194 return object_result_; | |
195 } | |
196 | |
197 const base::android::JavaRef<jclass>& | |
198 GinJavaMethodInvocationHelper::GetSafeAnnotationClass() { | |
199 return safe_annotation_clazz_; | |
200 } | |
201 | |
202 const GinJavaBridgeError GinJavaMethodInvocationHelper::GetInvocationError() { | |
203 return invocation_error_; | |
204 } | |
205 | |
206 void GinJavaMethodInvocationHelper::InvokeMethod(jobject object, | |
207 jclass clazz, | |
208 const JavaType& return_type, | |
209 jmethodID id, | |
210 jvalue* parameters) { | |
211 DCHECK(object || clazz); | |
212 JNIEnv* env = AttachCurrentThread(); | |
213 base::ListValue result_wrapper; | |
214 switch (return_type.type) { | |
215 case JavaType::TypeBoolean: | |
216 result_wrapper.AppendBoolean( | |
217 object ? env->CallBooleanMethodA(object, id, parameters) | |
218 : env->CallStaticBooleanMethodA(clazz, id, parameters)); | |
219 break; | |
220 case JavaType::TypeByte: | |
221 result_wrapper.AppendInteger( | |
222 object ? env->CallByteMethodA(object, id, parameters) | |
223 : env->CallStaticByteMethodA(clazz, id, parameters)); | |
224 break; | |
225 case JavaType::TypeChar: | |
226 result_wrapper.AppendInteger( | |
227 object ? env->CallCharMethodA(object, id, parameters) | |
228 : env->CallStaticCharMethodA(clazz, id, parameters)); | |
229 break; | |
230 case JavaType::TypeShort: | |
231 result_wrapper.AppendInteger( | |
232 object ? env->CallShortMethodA(object, id, parameters) | |
233 : env->CallStaticShortMethodA(clazz, id, parameters)); | |
234 break; | |
235 case JavaType::TypeInt: | |
236 result_wrapper.AppendInteger( | |
237 object ? env->CallIntMethodA(object, id, parameters) | |
238 : env->CallStaticIntMethodA(clazz, id, parameters)); | |
239 break; | |
240 case JavaType::TypeLong: | |
241 result_wrapper.AppendDouble( | |
242 object ? env->CallLongMethodA(object, id, parameters) | |
243 : env->CallStaticLongMethodA(clazz, id, parameters)); | |
244 break; | |
245 case JavaType::TypeFloat: { | |
246 float result = object | |
247 ? env->CallFloatMethodA(object, id, parameters) | |
248 : env->CallStaticFloatMethodA(clazz, id, parameters); | |
249 if (base::IsFinite(result)) { | |
250 result_wrapper.AppendDouble(result); | |
251 } else { | |
252 result_wrapper.Append( | |
253 GinJavaBridgeValue::CreateNonFiniteValue(result).release()); | |
254 } | |
255 break; | |
256 } | |
257 case JavaType::TypeDouble: { | |
258 double result = object | |
259 ? env->CallDoubleMethodA(object, id, parameters) | |
260 : env->CallStaticDoubleMethodA(clazz, id, parameters); | |
261 if (base::IsFinite(result)) { | |
262 result_wrapper.AppendDouble(result); | |
263 } else { | |
264 result_wrapper.Append( | |
265 GinJavaBridgeValue::CreateNonFiniteValue(result).release()); | |
266 } | |
267 break; | |
268 } | |
269 case JavaType::TypeVoid: | |
270 if (object) | |
271 env->CallVoidMethodA(object, id, parameters); | |
272 else | |
273 env->CallStaticVoidMethodA(clazz, id, parameters); | |
274 result_wrapper.Append( | |
275 GinJavaBridgeValue::CreateUndefinedValue().release()); | |
276 break; | |
277 case JavaType::TypeArray: | |
278 // LIVECONNECT_COMPLIANCE: Existing behavior is to not call methods that | |
279 // return arrays. Spec requires calling the method and converting the | |
280 // result to a JavaScript array. | |
281 result_wrapper.Append( | |
282 GinJavaBridgeValue::CreateUndefinedValue().release()); | |
283 break; | |
284 case JavaType::TypeString: { | |
285 jstring java_string = static_cast<jstring>( | |
286 object ? env->CallObjectMethodA(object, id, parameters) | |
287 : env->CallStaticObjectMethodA(clazz, id, parameters)); | |
288 // If an exception was raised, we must clear it before calling most JNI | |
289 // methods. ScopedJavaLocalRef is liable to make such calls, so we test | |
290 // first. | |
291 if (base::android::ClearException(env)) { | |
292 SetInvocationError(kGinJavaBridgeJavaExceptionRaised); | |
293 return; | |
294 } | |
295 ScopedJavaLocalRef<jstring> scoped_java_string(env, java_string); | |
296 if (!scoped_java_string.obj()) { | |
297 // LIVECONNECT_COMPLIANCE: Existing behavior is to return undefined. | |
298 // Spec requires returning a null string. | |
299 result_wrapper.Append( | |
300 GinJavaBridgeValue::CreateUndefinedValue().release()); | |
301 break; | |
302 } | |
303 result_wrapper.AppendString( | |
304 base::android::ConvertJavaStringToUTF8(scoped_java_string)); | |
305 break; | |
306 } | |
307 case JavaType::TypeObject: { | |
308 // If an exception was raised, we must clear it before calling most JNI | |
309 // methods. ScopedJavaLocalRef is liable to make such calls, so we test | |
310 // first. | |
311 jobject java_object = | |
312 object ? env->CallObjectMethodA(object, id, parameters) | |
313 : env->CallStaticObjectMethodA(clazz, id, parameters); | |
314 if (base::android::ClearException(env)) { | |
315 SetInvocationError(kGinJavaBridgeJavaExceptionRaised); | |
316 return; | |
317 } | |
318 ScopedJavaLocalRef<jobject> scoped_java_object(env, java_object); | |
319 if (!scoped_java_object.obj()) { | |
320 result_wrapper.Append(base::Value::CreateNullValue()); | |
321 break; | |
322 } | |
323 SetObjectResult(scoped_java_object, object_->GetSafeAnnotationClass()); | |
324 return; | |
325 } | |
326 } | |
327 // This is for all cases except JavaType::TypeObject. | |
328 if (!base::android::ClearException(env)) { | |
329 SetPrimitiveResult(result_wrapper); | |
330 } else { | |
331 SetInvocationError(kGinJavaBridgeJavaExceptionRaised); | |
332 } | |
333 } | |
334 | |
335 } // namespace content | |
OLD | NEW |