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