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"
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
;
27 // See frameworks/base/core/java/android/webkit/EventLogTags.logtags
28 const int kObjectGetClassInvocationAttemptLogTag
= 70151;
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();
62 if (AppendObjectRef(dispatcher
, *iter
))
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
);
81 if (AppendObjectRef(dispatcher
, &iter
.value()))
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
))
96 scoped_ptr
<const GinJavaBridgeValue
> value(
97 GinJavaBridgeValue::FromValue(raw_value
));
98 if (!value
->IsType(GinJavaBridgeValue::TYPE_OBJECT_ID
))
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
));
114 void GinJavaMethodInvocationHelper::Invoke() {
115 JNIEnv
* env
= AttachCurrentThread();
116 const JavaMethod
* method
=
117 object_
->FindMethod(method_name_
, arguments_
->GetSize());
119 SetInvocationError(kGinJavaBridgeMethodNotFound
);
123 if (object_
->IsObjectGetClassMethod(method
)) {
124 base::android::EventLogWriteInt(kObjectGetClassInvocationAttemptLogTag
,
126 SetInvocationError(kGinJavaBridgeAccessToObjectGetClassIsBlocked
);
130 ScopedJavaLocalRef
<jobject
> obj
;
131 ScopedJavaLocalRef
<jclass
> cls
;
132 if (method
->is_static()) {
133 cls
= object_
->GetLocalClassRef(env
);
135 obj
= object_
->GetLocalRef(env
);
137 if (obj
.is_null() && cls
.is_null()) {
138 SetInvocationError(kGinJavaBridgeObjectIsGone
);
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
,
149 method
->parameter_type(i
),
155 if (coercion_error
== kGinJavaBridgeNoError
) {
156 if (method
->is_static()) {
158 NULL
, cls
.obj(), method
->return_type(), method
->id(), ¶meters
[0]);
161 obj
.obj(), NULL
, method
->return_type(), method
->id(), ¶meters
[0]);
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
, ¶meters
[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
,
219 const JavaType
& return_type
,
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
));
231 case JavaType::TypeByte
:
232 result_wrapper
.AppendInteger(
233 object
? env
->CallByteMethodA(object
, id
, parameters
)
234 : env
->CallStaticByteMethodA(clazz
, id
, parameters
));
236 case JavaType::TypeChar
:
237 result_wrapper
.AppendInteger(
238 object
? env
->CallCharMethodA(object
, id
, parameters
)
239 : env
->CallStaticCharMethodA(clazz
, id
, parameters
));
241 case JavaType::TypeShort
:
242 result_wrapper
.AppendInteger(
243 object
? env
->CallShortMethodA(object
, id
, parameters
)
244 : env
->CallStaticShortMethodA(clazz
, id
, parameters
));
246 case JavaType::TypeInt
:
247 result_wrapper
.AppendInteger(
248 object
? env
->CallIntMethodA(object
, id
, parameters
)
249 : env
->CallStaticIntMethodA(clazz
, id
, parameters
));
251 case JavaType::TypeLong
:
252 result_wrapper
.AppendDouble(
253 object
? env
->CallLongMethodA(object
, id
, parameters
)
254 : env
->CallStaticLongMethodA(clazz
, id
, parameters
));
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
);
263 result_wrapper
.Append(
264 GinJavaBridgeValue::CreateNonFiniteValue(result
).release());
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
);
275 result_wrapper
.Append(
276 GinJavaBridgeValue::CreateNonFiniteValue(result
).release());
280 case JavaType::TypeVoid
:
282 env
->CallVoidMethodA(object
, id
, parameters
);
284 env
->CallStaticVoidMethodA(clazz
, id
, parameters
);
285 result_wrapper
.Append(
286 GinJavaBridgeValue::CreateUndefinedValue().release());
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());
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
302 if (base::android::ClearException(env
)) {
303 SetInvocationError(kGinJavaBridgeJavaExceptionRaised
);
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());
314 result_wrapper
.AppendString(
315 base::android::ConvertJavaStringToUTF8(scoped_java_string
));
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
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
);
329 ScopedJavaLocalRef
<jobject
> scoped_java_object(env
, java_object
);
330 if (!scoped_java_object
.obj()) {
331 result_wrapper
.Append(base::Value::CreateNullValue());
334 SetObjectResult(scoped_java_object
, object_
->GetSafeAnnotationClass());
338 // This is for all cases except JavaType::TypeObject.
339 if (!base::android::ClearException(env
)) {
340 SetPrimitiveResult(result_wrapper
);
342 SetInvocationError(kGinJavaBridgeJavaExceptionRaised
);
346 } // namespace content