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/float_util.h"
12 #include "content/browser/android/java/gin_java_script_to_java_types_coercion.h"
13 #include "content/browser/android/java/java_method.h"
14 #include "content/browser/android/java/jni_helper.h"
15 #include "content/common/android/gin_java_bridge_value.h"
16 #include "content/public/browser/browser_thread.h"
17 #include "third_party/WebKit/public/platform/WebString.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;
29 // This is an intermediate solution until we fix http://crbug.com/391492.
30 std::string
ConvertJavaStringToUTF8(JNIEnv
* env
, jstring str
) {
31 const jchar
* chars
= env
->GetStringChars(str
, NULL
);
33 blink::WebString
utf16(chars
, env
->GetStringLength(str
));
34 env
->ReleaseStringChars(str
, chars
);
40 GinJavaMethodInvocationHelper::GinJavaMethodInvocationHelper(
41 scoped_ptr
<ObjectDelegate
> object
,
42 const std::string
& method_name
,
43 const base::ListValue
& arguments
)
44 : object_(object
.Pass()),
45 method_name_(method_name
),
46 arguments_(arguments
.DeepCopy()),
47 invocation_error_(kGinJavaBridgeNoError
) {
50 GinJavaMethodInvocationHelper::~GinJavaMethodInvocationHelper() {}
52 void GinJavaMethodInvocationHelper::Init(DispatcherDelegate
* dispatcher
) {
53 // Build on the UI thread a map of object_id -> WeakRef for Java objects from
54 // |arguments_|. Then we can use this map on the background thread without
55 // accessing |dispatcher|.
56 BuildObjectRefsFromListValue(dispatcher
, arguments_
.get());
59 // As V8ValueConverter has finite recursion depth when serializing
60 // JavaScript values, we don't bother about having a recursion threshold here.
61 void GinJavaMethodInvocationHelper::BuildObjectRefsFromListValue(
62 DispatcherDelegate
* dispatcher
,
63 const base::Value
* list_value
) {
64 DCHECK(list_value
->IsType(base::Value::TYPE_LIST
));
65 const base::ListValue
* list
;
66 list_value
->GetAsList(&list
);
67 for (base::ListValue::const_iterator iter
= list
->begin();
70 if (AppendObjectRef(dispatcher
, *iter
))
72 if ((*iter
)->IsType(base::Value::TYPE_LIST
)) {
73 BuildObjectRefsFromListValue(dispatcher
, *iter
);
74 } else if ((*iter
)->IsType(base::Value::TYPE_DICTIONARY
)) {
75 BuildObjectRefsFromDictionaryValue(dispatcher
, *iter
);
80 void GinJavaMethodInvocationHelper::BuildObjectRefsFromDictionaryValue(
81 DispatcherDelegate
* dispatcher
,
82 const base::Value
* dict_value
) {
83 DCHECK(dict_value
->IsType(base::Value::TYPE_DICTIONARY
));
84 const base::DictionaryValue
* dict
;
85 dict_value
->GetAsDictionary(&dict
);
86 for (base::DictionaryValue::Iterator
iter(*dict
);
89 if (AppendObjectRef(dispatcher
, &iter
.value()))
91 if (iter
.value().IsType(base::Value::TYPE_LIST
)) {
92 BuildObjectRefsFromListValue(dispatcher
, &iter
.value());
93 } else if (iter
.value().IsType(base::Value::TYPE_DICTIONARY
)) {
94 BuildObjectRefsFromDictionaryValue(dispatcher
, &iter
.value());
99 bool GinJavaMethodInvocationHelper::AppendObjectRef(
100 DispatcherDelegate
* dispatcher
,
101 const base::Value
* raw_value
) {
102 if (!GinJavaBridgeValue::ContainsGinJavaBridgeValue(raw_value
))
104 scoped_ptr
<const GinJavaBridgeValue
> value(
105 GinJavaBridgeValue::FromValue(raw_value
));
106 if (!value
->IsType(GinJavaBridgeValue::TYPE_OBJECT_ID
))
108 GinJavaBoundObject::ObjectID object_id
;
109 if (value
->GetAsObjectID(&object_id
)) {
110 ObjectRefs::iterator iter
= object_refs_
.find(object_id
);
111 if (iter
== object_refs_
.end()) {
112 JavaObjectWeakGlobalRef
object_ref(
113 dispatcher
->GetObjectWeakRef(object_id
));
114 if (!object_ref
.is_empty()) {
115 object_refs_
.insert(std::make_pair(object_id
, object_ref
));
122 void GinJavaMethodInvocationHelper::Invoke() {
123 JNIEnv
* env
= AttachCurrentThread();
124 const JavaMethod
* method
=
125 object_
->FindMethod(method_name_
, arguments_
->GetSize());
127 SetInvocationError(kGinJavaBridgeMethodNotFound
);
131 if (object_
->IsObjectGetClassMethod(method
)) {
132 base::android::EventLogWriteInt(kObjectGetClassInvocationAttemptLogTag
,
134 SetInvocationError(kGinJavaBridgeAccessToObjectGetClassIsBlocked
);
138 ScopedJavaLocalRef
<jobject
> obj
;
139 ScopedJavaLocalRef
<jclass
> cls
;
140 if (method
->is_static()) {
141 cls
= object_
->GetLocalClassRef(env
);
143 obj
= object_
->GetLocalRef(env
);
145 if (obj
.is_null() && cls
.is_null()) {
146 SetInvocationError(kGinJavaBridgeObjectIsGone
);
150 GinJavaBridgeError coercion_error
= kGinJavaBridgeNoError
;
151 std::vector
<jvalue
> parameters(method
->num_parameters());
152 for (size_t i
= 0; i
< method
->num_parameters(); ++i
) {
153 const base::Value
* argument
;
154 arguments_
->Get(i
, &argument
);
155 parameters
[i
] = CoerceJavaScriptValueToJavaValue(env
,
157 method
->parameter_type(i
),
163 if (coercion_error
== kGinJavaBridgeNoError
) {
164 if (method
->is_static()) {
166 NULL
, cls
.obj(), method
->return_type(), method
->id(), ¶meters
[0]);
169 obj
.obj(), NULL
, method
->return_type(), method
->id(), ¶meters
[0]);
172 SetInvocationError(coercion_error
);
175 // Now that we're done with the jvalue, release any local references created
176 // by CoerceJavaScriptValueToJavaValue().
177 for (size_t i
= 0; i
< method
->num_parameters(); ++i
) {
178 ReleaseJavaValueIfRequired(env
, ¶meters
[i
], method
->parameter_type(i
));
182 void GinJavaMethodInvocationHelper::SetInvocationError(
183 GinJavaBridgeError error
) {
184 holds_primitive_result_
= true;
185 primitive_result_
.reset(new base::ListValue());
186 invocation_error_
= error
;
189 void GinJavaMethodInvocationHelper::SetPrimitiveResult(
190 const base::ListValue
& result_wrapper
) {
191 holds_primitive_result_
= true;
192 primitive_result_
.reset(result_wrapper
.DeepCopy());
195 void GinJavaMethodInvocationHelper::SetObjectResult(
196 const base::android::JavaRef
<jobject
>& object
,
197 const base::android::JavaRef
<jclass
>& safe_annotation_clazz
) {
198 holds_primitive_result_
= false;
199 object_result_
.Reset(object
);
200 safe_annotation_clazz_
.Reset(safe_annotation_clazz
);
203 bool GinJavaMethodInvocationHelper::HoldsPrimitiveResult() {
204 return holds_primitive_result_
;
207 const base::ListValue
& GinJavaMethodInvocationHelper::GetPrimitiveResult() {
208 return *primitive_result_
.get();
211 const base::android::JavaRef
<jobject
>&
212 GinJavaMethodInvocationHelper::GetObjectResult() {
213 return object_result_
;
216 const base::android::JavaRef
<jclass
>&
217 GinJavaMethodInvocationHelper::GetSafeAnnotationClass() {
218 return safe_annotation_clazz_
;
221 const GinJavaBridgeError
GinJavaMethodInvocationHelper::GetInvocationError() {
222 return invocation_error_
;
225 void GinJavaMethodInvocationHelper::InvokeMethod(jobject object
,
227 const JavaType
& return_type
,
229 jvalue
* parameters
) {
230 DCHECK(object
|| clazz
);
231 JNIEnv
* env
= AttachCurrentThread();
232 base::ListValue result_wrapper
;
233 switch (return_type
.type
) {
234 case JavaType::TypeBoolean
:
235 result_wrapper
.AppendBoolean(
236 object
? env
->CallBooleanMethodA(object
, id
, parameters
)
237 : env
->CallStaticBooleanMethodA(clazz
, id
, parameters
));
239 case JavaType::TypeByte
:
240 result_wrapper
.AppendInteger(
241 object
? env
->CallByteMethodA(object
, id
, parameters
)
242 : env
->CallStaticByteMethodA(clazz
, id
, parameters
));
244 case JavaType::TypeChar
:
245 result_wrapper
.AppendInteger(
246 object
? env
->CallCharMethodA(object
, id
, parameters
)
247 : env
->CallStaticCharMethodA(clazz
, id
, parameters
));
249 case JavaType::TypeShort
:
250 result_wrapper
.AppendInteger(
251 object
? env
->CallShortMethodA(object
, id
, parameters
)
252 : env
->CallStaticShortMethodA(clazz
, id
, parameters
));
254 case JavaType::TypeInt
:
255 result_wrapper
.AppendInteger(
256 object
? env
->CallIntMethodA(object
, id
, parameters
)
257 : env
->CallStaticIntMethodA(clazz
, id
, parameters
));
259 case JavaType::TypeLong
:
260 result_wrapper
.AppendDouble(
261 object
? env
->CallLongMethodA(object
, id
, parameters
)
262 : env
->CallStaticLongMethodA(clazz
, id
, parameters
));
264 case JavaType::TypeFloat
: {
265 float result
= object
266 ? env
->CallFloatMethodA(object
, id
, parameters
)
267 : env
->CallStaticFloatMethodA(clazz
, id
, parameters
);
268 if (base::IsFinite(result
)) {
269 result_wrapper
.AppendDouble(result
);
271 result_wrapper
.Append(
272 GinJavaBridgeValue::CreateNonFiniteValue(result
).release());
276 case JavaType::TypeDouble
: {
277 double result
= object
278 ? env
->CallDoubleMethodA(object
, id
, parameters
)
279 : env
->CallStaticDoubleMethodA(clazz
, id
, parameters
);
280 if (base::IsFinite(result
)) {
281 result_wrapper
.AppendDouble(result
);
283 result_wrapper
.Append(
284 GinJavaBridgeValue::CreateNonFiniteValue(result
).release());
288 case JavaType::TypeVoid
:
290 env
->CallVoidMethodA(object
, id
, parameters
);
292 env
->CallStaticVoidMethodA(clazz
, id
, parameters
);
293 result_wrapper
.Append(
294 GinJavaBridgeValue::CreateUndefinedValue().release());
296 case JavaType::TypeArray
:
297 // LIVECONNECT_COMPLIANCE: Existing behavior is to not call methods that
298 // return arrays. Spec requires calling the method and converting the
299 // result to a JavaScript array.
300 result_wrapper
.Append(
301 GinJavaBridgeValue::CreateUndefinedValue().release());
303 case JavaType::TypeString
: {
304 jstring java_string
= static_cast<jstring
>(
305 object
? env
->CallObjectMethodA(object
, id
, parameters
)
306 : env
->CallStaticObjectMethodA(clazz
, id
, parameters
));
307 // If an exception was raised, we must clear it before calling most JNI
308 // methods. ScopedJavaLocalRef is liable to make such calls, so we test
310 if (base::android::ClearException(env
)) {
311 SetInvocationError(kGinJavaBridgeJavaExceptionRaised
);
314 ScopedJavaLocalRef
<jstring
> scoped_java_string(env
, java_string
);
315 if (!scoped_java_string
.obj()) {
316 // LIVECONNECT_COMPLIANCE: Existing behavior is to return undefined.
317 // Spec requires returning a null string.
318 result_wrapper
.Append(
319 GinJavaBridgeValue::CreateUndefinedValue().release());
322 result_wrapper
.AppendString(
323 ConvertJavaStringToUTF8(env
, scoped_java_string
.obj()));
326 case JavaType::TypeObject
: {
327 // If an exception was raised, we must clear it before calling most JNI
328 // methods. ScopedJavaLocalRef is liable to make such calls, so we test
330 jobject java_object
=
331 object
? env
->CallObjectMethodA(object
, id
, parameters
)
332 : env
->CallStaticObjectMethodA(clazz
, id
, parameters
);
333 if (base::android::ClearException(env
)) {
334 SetInvocationError(kGinJavaBridgeJavaExceptionRaised
);
337 ScopedJavaLocalRef
<jobject
> scoped_java_object(env
, java_object
);
338 if (!scoped_java_object
.obj()) {
339 result_wrapper
.Append(base::Value::CreateNullValue());
342 SetObjectResult(scoped_java_object
, object_
->GetSafeAnnotationClass());
346 // This is for all cases except JavaType::TypeObject.
347 if (!base::android::ClearException(env
)) {
348 SetPrimitiveResult(result_wrapper
);
350 SetInvocationError(kGinJavaBridgeJavaExceptionRaised
);
354 } // namespace content