Re-subimission of https://codereview.chromium.org/1041213003/
[chromium-blink-merge.git] / content / browser / android / java / gin_java_method_invocation_helper.cc
blobae1c798fe52b85ba8b7fbe1ead424afde596588d
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 "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;
22 namespace content {
24 namespace {
26 // See frameworks/base/core/java/android/webkit/EventLogTags.logtags
27 const int kObjectGetClassInvocationAttemptLogTag = 70151;
29 } // namespace
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();
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);
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());
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));
110 return true;
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;
122 if (object_->IsObjectGetClassMethod(method)) {
123 base::android::EventLogWriteInt(kObjectGetClassInvocationAttemptLogTag,
124 getuid());
125 SetInvocationError(kGinJavaBridgeAccessToObjectGetClassIsBlocked);
126 return;
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);
136 if (obj.is_null() && cls.is_null()) {
137 SetInvocationError(kGinJavaBridgeObjectIsGone);
138 return;
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,
147 argument,
148 method->parameter_type(i),
149 true,
150 object_refs_,
151 &coercion_error);
154 if (coercion_error == kGinJavaBridgeNoError) {
155 if (method->is_static()) {
156 InvokeMethod(
157 NULL, cls.obj(), method->return_type(), method->id(), &parameters[0]);
158 } else {
159 InvokeMethod(
160 obj.obj(), NULL, method->return_type(), method->id(), &parameters[0]);
162 } else {
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, &parameters[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,
217 jclass clazz,
218 const JavaType& return_type,
219 jmethodID id,
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));
229 break;
230 case JavaType::TypeByte:
231 result_wrapper.AppendInteger(
232 object ? env->CallByteMethodA(object, id, parameters)
233 : env->CallStaticByteMethodA(clazz, id, parameters));
234 break;
235 case JavaType::TypeChar:
236 result_wrapper.AppendInteger(
237 object ? env->CallCharMethodA(object, id, parameters)
238 : env->CallStaticCharMethodA(clazz, id, parameters));
239 break;
240 case JavaType::TypeShort:
241 result_wrapper.AppendInteger(
242 object ? env->CallShortMethodA(object, id, parameters)
243 : env->CallStaticShortMethodA(clazz, id, parameters));
244 break;
245 case JavaType::TypeInt:
246 result_wrapper.AppendInteger(
247 object ? env->CallIntMethodA(object, id, parameters)
248 : env->CallStaticIntMethodA(clazz, id, parameters));
249 break;
250 case JavaType::TypeLong:
251 result_wrapper.AppendDouble(
252 object ? env->CallLongMethodA(object, id, parameters)
253 : env->CallStaticLongMethodA(clazz, id, parameters));
254 break;
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);
261 } else {
262 result_wrapper.Append(
263 GinJavaBridgeValue::CreateNonFiniteValue(result).release());
265 break;
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);
273 } else {
274 result_wrapper.Append(
275 GinJavaBridgeValue::CreateNonFiniteValue(result).release());
277 break;
279 case JavaType::TypeVoid:
280 if (object)
281 env->CallVoidMethodA(object, id, parameters);
282 else
283 env->CallStaticVoidMethodA(clazz, id, parameters);
284 result_wrapper.Append(
285 GinJavaBridgeValue::CreateUndefinedValue().release());
286 break;
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());
293 break;
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
300 // first.
301 if (base::android::ClearException(env)) {
302 SetInvocationError(kGinJavaBridgeJavaExceptionRaised);
303 return;
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());
311 break;
313 result_wrapper.AppendString(
314 base::android::ConvertJavaStringToUTF8(scoped_java_string));
315 break;
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
320 // first.
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);
326 return;
328 ScopedJavaLocalRef<jobject> scoped_java_object(env, java_object);
329 if (!scoped_java_object.obj()) {
330 result_wrapper.Append(base::Value::CreateNullValue());
331 break;
333 SetObjectResult(scoped_java_object, object_->GetSafeAnnotationClass());
334 return;
337 // This is for all cases except JavaType::TypeObject.
338 if (!base::android::ClearException(env)) {
339 SetPrimitiveResult(result_wrapper);
340 } else {
341 SetInvocationError(kGinJavaBridgeJavaExceptionRaised);
345 } // namespace content