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"
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/android/java/gin_java_script_to_java_types_coercion.h"
14 #include "content/browser/android/java/java_method.h"
15 #include "content/browser/android/java/jni_helper.h"
16 #include "content/common/android/gin_java_bridge_value.h"
17 #include "content/public/browser/browser_thread.h"
19 using base::android::AttachCurrentThread
;
20 using base::android::ScopedJavaLocalRef
;
26 // See frameworks/base/core/java/android/webkit/EventLogTags.logtags
27 const int kObjectGetClassInvocationAttemptLogTag
= 70151;
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
) {
41 GinJavaMethodInvocationHelper::~GinJavaMethodInvocationHelper() {}
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());
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();
61 if (AppendObjectRef(dispatcher
, *iter
))
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
);
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
);
80 if (AppendObjectRef(dispatcher
, &iter
.value()))
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());
90 bool GinJavaMethodInvocationHelper::AppendObjectRef(
91 DispatcherDelegate
* dispatcher
,
92 const base::Value
* raw_value
) {
93 if (!GinJavaBridgeValue::ContainsGinJavaBridgeValue(raw_value
))
95 scoped_ptr
<const GinJavaBridgeValue
> value(
96 GinJavaBridgeValue::FromValue(raw_value
));
97 if (!value
->IsType(GinJavaBridgeValue::TYPE_OBJECT_ID
))
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
));
113 void GinJavaMethodInvocationHelper::Invoke() {
114 JNIEnv
* env
= AttachCurrentThread();
115 const JavaMethod
* method
=
116 object_
->FindMethod(method_name_
, arguments_
->GetSize());
118 SetInvocationError(kGinJavaBridgeMethodNotFound
);
122 if (object_
->IsObjectGetClassMethod(method
)) {
123 base::android::EventLogWriteInt(kObjectGetClassInvocationAttemptLogTag
,
125 SetInvocationError(kGinJavaBridgeAccessToObjectGetClassIsBlocked
);
129 ScopedJavaLocalRef
<jobject
> obj
;
130 ScopedJavaLocalRef
<jclass
> cls
;
131 if (method
->is_static()) {
132 cls
= object_
->GetLocalClassRef(env
);
134 obj
= object_
->GetLocalRef(env
);
136 if (obj
.is_null() && cls
.is_null()) {
137 SetInvocationError(kGinJavaBridgeObjectIsGone
);
141 GinJavaBridgeError coercion_error
= kGinJavaBridgeNoError
;
142 std::vector
<jvalue
> parameters(method
->num_parameters());
143 for (size_t i
= 0; i
< method
->num_parameters(); ++i
) {
144 const base::Value
* argument
;
145 arguments_
->Get(i
, &argument
);
146 parameters
[i
] = CoerceJavaScriptValueToJavaValue(env
,
148 method
->parameter_type(i
),
154 if (coercion_error
== kGinJavaBridgeNoError
) {
155 if (method
->is_static()) {
157 NULL
, cls
.obj(), method
->return_type(), method
->id(), ¶meters
[0]);
160 obj
.obj(), NULL
, method
->return_type(), method
->id(), ¶meters
[0]);
163 SetInvocationError(coercion_error
);
166 // Now that we're done with the jvalue, release any local references created
167 // by CoerceJavaScriptValueToJavaValue().
168 for (size_t i
= 0; i
< method
->num_parameters(); ++i
) {
169 ReleaseJavaValueIfRequired(env
, ¶meters
[i
], method
->parameter_type(i
));
173 void GinJavaMethodInvocationHelper::SetInvocationError(
174 GinJavaBridgeError error
) {
175 holds_primitive_result_
= true;
176 primitive_result_
.reset(new base::ListValue());
177 invocation_error_
= error
;
180 void GinJavaMethodInvocationHelper::SetPrimitiveResult(
181 const base::ListValue
& result_wrapper
) {
182 holds_primitive_result_
= true;
183 primitive_result_
.reset(result_wrapper
.DeepCopy());
186 void GinJavaMethodInvocationHelper::SetObjectResult(
187 const base::android::JavaRef
<jobject
>& object
,
188 const base::android::JavaRef
<jclass
>& safe_annotation_clazz
) {
189 holds_primitive_result_
= false;
190 object_result_
.Reset(object
);
191 safe_annotation_clazz_
.Reset(safe_annotation_clazz
);
194 bool GinJavaMethodInvocationHelper::HoldsPrimitiveResult() {
195 return holds_primitive_result_
;
198 const base::ListValue
& GinJavaMethodInvocationHelper::GetPrimitiveResult() {
199 return *primitive_result_
.get();
202 const base::android::JavaRef
<jobject
>&
203 GinJavaMethodInvocationHelper::GetObjectResult() {
204 return object_result_
;
207 const base::android::JavaRef
<jclass
>&
208 GinJavaMethodInvocationHelper::GetSafeAnnotationClass() {
209 return safe_annotation_clazz_
;
212 const GinJavaBridgeError
GinJavaMethodInvocationHelper::GetInvocationError() {
213 return invocation_error_
;
216 void GinJavaMethodInvocationHelper::InvokeMethod(jobject object
,
218 const JavaType
& return_type
,
220 jvalue
* parameters
) {
221 DCHECK(object
|| clazz
);
222 JNIEnv
* env
= AttachCurrentThread();
223 base::ListValue result_wrapper
;
224 switch (return_type
.type
) {
225 case JavaType::TypeBoolean
:
226 result_wrapper
.AppendBoolean(
227 object
? env
->CallBooleanMethodA(object
, id
, parameters
)
228 : env
->CallStaticBooleanMethodA(clazz
, id
, parameters
));
230 case JavaType::TypeByte
:
231 result_wrapper
.AppendInteger(
232 object
? env
->CallByteMethodA(object
, id
, parameters
)
233 : env
->CallStaticByteMethodA(clazz
, id
, parameters
));
235 case JavaType::TypeChar
:
236 result_wrapper
.AppendInteger(
237 object
? env
->CallCharMethodA(object
, id
, parameters
)
238 : env
->CallStaticCharMethodA(clazz
, id
, parameters
));
240 case JavaType::TypeShort
:
241 result_wrapper
.AppendInteger(
242 object
? env
->CallShortMethodA(object
, id
, parameters
)
243 : env
->CallStaticShortMethodA(clazz
, id
, parameters
));
245 case JavaType::TypeInt
:
246 result_wrapper
.AppendInteger(
247 object
? env
->CallIntMethodA(object
, id
, parameters
)
248 : env
->CallStaticIntMethodA(clazz
, id
, parameters
));
250 case JavaType::TypeLong
:
251 result_wrapper
.AppendDouble(
252 object
? env
->CallLongMethodA(object
, id
, parameters
)
253 : env
->CallStaticLongMethodA(clazz
, id
, parameters
));
255 case JavaType::TypeFloat
: {
256 float result
= object
257 ? env
->CallFloatMethodA(object
, id
, parameters
)
258 : env
->CallStaticFloatMethodA(clazz
, id
, parameters
);
259 if (base::IsFinite(result
)) {
260 result_wrapper
.AppendDouble(result
);
262 result_wrapper
.Append(
263 GinJavaBridgeValue::CreateNonFiniteValue(result
).release());
267 case JavaType::TypeDouble
: {
268 double result
= object
269 ? env
->CallDoubleMethodA(object
, id
, parameters
)
270 : env
->CallStaticDoubleMethodA(clazz
, id
, parameters
);
271 if (base::IsFinite(result
)) {
272 result_wrapper
.AppendDouble(result
);
274 result_wrapper
.Append(
275 GinJavaBridgeValue::CreateNonFiniteValue(result
).release());
279 case JavaType::TypeVoid
:
281 env
->CallVoidMethodA(object
, id
, parameters
);
283 env
->CallStaticVoidMethodA(clazz
, id
, parameters
);
284 result_wrapper
.Append(
285 GinJavaBridgeValue::CreateUndefinedValue().release());
287 case JavaType::TypeArray
:
288 // LIVECONNECT_COMPLIANCE: Existing behavior is to not call methods that
289 // return arrays. Spec requires calling the method and converting the
290 // result to a JavaScript array.
291 result_wrapper
.Append(
292 GinJavaBridgeValue::CreateUndefinedValue().release());
294 case JavaType::TypeString
: {
295 jstring java_string
= static_cast<jstring
>(
296 object
? env
->CallObjectMethodA(object
, id
, parameters
)
297 : env
->CallStaticObjectMethodA(clazz
, id
, parameters
));
298 // If an exception was raised, we must clear it before calling most JNI
299 // methods. ScopedJavaLocalRef is liable to make such calls, so we test
301 if (base::android::ClearException(env
)) {
302 SetInvocationError(kGinJavaBridgeJavaExceptionRaised
);
305 ScopedJavaLocalRef
<jstring
> scoped_java_string(env
, java_string
);
306 if (!scoped_java_string
.obj()) {
307 // LIVECONNECT_COMPLIANCE: Existing behavior is to return undefined.
308 // Spec requires returning a null string.
309 result_wrapper
.Append(
310 GinJavaBridgeValue::CreateUndefinedValue().release());
313 result_wrapper
.AppendString(
314 base::android::ConvertJavaStringToUTF8(scoped_java_string
));
317 case JavaType::TypeObject
: {
318 // If an exception was raised, we must clear it before calling most JNI
319 // methods. ScopedJavaLocalRef is liable to make such calls, so we test
321 jobject java_object
=
322 object
? env
->CallObjectMethodA(object
, id
, parameters
)
323 : env
->CallStaticObjectMethodA(clazz
, id
, parameters
);
324 if (base::android::ClearException(env
)) {
325 SetInvocationError(kGinJavaBridgeJavaExceptionRaised
);
328 ScopedJavaLocalRef
<jobject
> scoped_java_object(env
, java_object
);
329 if (!scoped_java_object
.obj()) {
330 result_wrapper
.Append(base::Value::CreateNullValue());
333 SetObjectResult(scoped_java_object
, object_
->GetSafeAnnotationClass());
337 // This is for all cases except JavaType::TypeObject.
338 if (!base::android::ClearException(env
)) {
339 SetPrimitiveResult(result_wrapper
);
341 SetInvocationError(kGinJavaBridgeJavaExceptionRaised
);
345 } // namespace content