Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(732)

Side by Side Diff: content/browser/renderer_host/java/gin_java_method_invocation_helper.cc

Issue 302173006: [Android] Java Bridge with Gin: implement Java methods invocation (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src
Patch Set: Fixed compilation of tests Created 6 years, 6 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch | Annotate | Revision Log
OLDNEW
(Empty)
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.
4
5 #include "content/browser/renderer_host/java/gin_java_method_invocation_helper.h "
6
7 #include <unistd.h>
8
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/renderer_host/java/gin_java_script_to_java_types_coerc ion.h"
14 #include "content/browser/renderer_host/java/java_method.h"
15 #include "content/browser/renderer_host/java/jni_helper.h"
16 #include "content/common/android/gin_java_bridge_value.h"
17 #include "content/public/browser/browser_thread.h"
18
19 using base::android::AttachCurrentThread;
20 using base::android::ScopedJavaLocalRef;
21
22 namespace content {
23
24 namespace {
25
26 const char kObjectIsGone[] = "Java object is gone";
27 const char kMethodNotFound[] = "Method not found";
28 const char kAccessToObjectGetClassIsBlocked[] =
29 "Access to java.lang.Object.getClass is blocked";
30 const char kJavaExceptionRaised[] =
31 "Java exception has been raised during method invocation";
32
33 // See frameworks/base/core/java/android/webkit/EventLogTags.logtags
34 const int kObjectGetClassInvocationAttemptLogTag = 70151;
35
36 } // namespace
37
38 GinJavaMethodInvocationHelper::GinJavaMethodInvocationHelper(
39 scoped_ptr<ObjectDelegate> object,
40 const std::string& method_name,
41 const base::ListValue& arguments)
42 : object_(object.Pass()),
43 method_name_(method_name),
44 arguments_(arguments.DeepCopy()) {
45 }
46
47 GinJavaMethodInvocationHelper::~GinJavaMethodInvocationHelper() {}
48
49 void GinJavaMethodInvocationHelper::Init(DispatcherDelegate* dispatcher) {
50 // Build on the UI thread a map of object_id -> WeakRef for Java objects from
51 // |arguments_|. Then we can use this map on the background thread without
52 // accessing |dispatcher|.
53 BuildObjectRefsFromListValue(dispatcher, arguments_.get());
54 }
55
56 // As V8ValueConverter has finite recursion depth when serializing
57 // JavaScript values, we don't bother about having a recursion threshold here.
58 void GinJavaMethodInvocationHelper::BuildObjectRefsFromListValue(
59 DispatcherDelegate* dispatcher,
60 const base::Value* list_value) {
61 DCHECK(list_value->IsType(base::Value::TYPE_LIST));
62 const base::ListValue* list;
63 list_value->GetAsList(&list);
64 for (base::ListValue::const_iterator iter = list->begin();
65 iter != list->end();
66 ++iter) {
67 if (AppendObjectRef(dispatcher, *iter))
68 continue;
69 if ((*iter)->IsType(base::Value::TYPE_LIST)) {
70 BuildObjectRefsFromListValue(dispatcher, *iter);
71 } else if ((*iter)->IsType(base::Value::TYPE_DICTIONARY)) {
72 BuildObjectRefsFromDictionaryValue(dispatcher, *iter);
73 }
74 }
75 }
76
77 void GinJavaMethodInvocationHelper::BuildObjectRefsFromDictionaryValue(
78 DispatcherDelegate* dispatcher,
79 const base::Value* dict_value) {
80 DCHECK(dict_value->IsType(base::Value::TYPE_DICTIONARY));
81 const base::DictionaryValue* dict;
82 dict_value->GetAsDictionary(&dict);
83 for (base::DictionaryValue::Iterator iter(*dict);
84 !iter.IsAtEnd();
85 iter.Advance()) {
86 if (AppendObjectRef(dispatcher, &iter.value()))
87 continue;
88 if (iter.value().IsType(base::Value::TYPE_LIST)) {
89 BuildObjectRefsFromListValue(dispatcher, &iter.value());
90 } else if (iter.value().IsType(base::Value::TYPE_DICTIONARY)) {
91 BuildObjectRefsFromDictionaryValue(dispatcher, &iter.value());
92 }
93 }
94 }
95
96 bool GinJavaMethodInvocationHelper::AppendObjectRef(
97 DispatcherDelegate* dispatcher,
98 const base::Value* raw_value) {
99 if (!GinJavaBridgeValue::ContainsGinJavaBridgeValue(raw_value))
100 return false;
101 scoped_ptr<const GinJavaBridgeValue> value(
102 GinJavaBridgeValue::FromValue(raw_value));
103 if (!value->IsType(GinJavaBridgeValue::TYPE_OBJECT_ID))
104 return false;
105 GinJavaBoundObject::ObjectID object_id;
106 if (value->GetAsObjectID(&object_id)) {
107 ObjectRefs::iterator iter = object_refs_.find(object_id);
108 if (iter == object_refs_.end()) {
109 JavaObjectWeakGlobalRef object_ref(
110 dispatcher->GetObjectWeakRef(object_id));
111 if (!object_ref.is_empty()) {
112 object_refs_.insert(std::make_pair(object_id, object_ref));
113 }
114 }
115 }
116 return true;
117 }
118
119 void GinJavaMethodInvocationHelper::Invoke() {
120 JNIEnv* env = AttachCurrentThread();
121 ScopedJavaLocalRef<jobject> obj(object_->GetLocalRef(env));
122 if (obj.is_null()) {
123 SetInvocationFailure(kObjectIsGone);
124 return;
125 }
126 const JavaMethod* method =
127 object_->FindMethod(method_name_, arguments_->GetSize());
128 if (!method) {
129 SetInvocationFailure(kMethodNotFound);
130 return;
131 }
132
133 if (object_->IsObjectGetClassMethod(method)) {
134 base::android::EventLogWriteInt(kObjectGetClassInvocationAttemptLogTag,
135 getuid());
136 SetInvocationFailure(kAccessToObjectGetClassIsBlocked);
137 return;
138 }
139
140 std::vector<jvalue> parameters(method->num_parameters());
141 for (size_t i = 0; i < method->num_parameters(); ++i) {
142 const base::Value* argument;
143 arguments_->Get(i, &argument);
144 parameters[i] = CoerceJavaScriptValueToJavaValue(
145 env, argument, method->parameter_type(i), true, object_refs_);
146 }
147 InvokeMethod(obj.obj(), method->return_type(), method->id(), &parameters[0]);
148
149 // Now that we're done with the jvalue, release any local references created
150 // by CoerceJavaScriptValueToJavaValue().
151 for (size_t i = 0; i < method->num_parameters(); ++i) {
152 ReleaseJavaValueIfRequired(env, &parameters[i], method->parameter_type(i));
153 }
154 }
155
156 void GinJavaMethodInvocationHelper::SetInvocationFailure(
157 const char* error_message) {
158 holds_primitive_result_ = true;
159 primitive_result_.reset(new base::ListValue());
160 error_message_ = error_message;
161 }
162
163 void GinJavaMethodInvocationHelper::SetPrimitiveResult(
164 const base::ListValue& result_wrapper) {
165 holds_primitive_result_ = true;
166 primitive_result_.reset(result_wrapper.DeepCopy());
167 }
168
169 void GinJavaMethodInvocationHelper::SetObjectResult(
170 const base::android::JavaRef<jobject>& object,
171 const base::android::JavaRef<jclass>& safe_annotation_clazz) {
172 holds_primitive_result_ = false;
173 object_result_.Reset(object);
174 safe_annotation_clazz_.Reset(safe_annotation_clazz);
175 }
176
177 bool GinJavaMethodInvocationHelper::HoldsPrimitiveResult() {
178 return holds_primitive_result_;
179 }
180
181 const base::ListValue& GinJavaMethodInvocationHelper::GetPrimitiveResult() {
182 return *primitive_result_.get();
183 }
184
185 const base::android::JavaRef<jobject>&
186 GinJavaMethodInvocationHelper::GetObjectResult() {
187 return object_result_;
188 }
189
190 const base::android::JavaRef<jclass>&
191 GinJavaMethodInvocationHelper::GetSafeAnnotationClass() {
192 return safe_annotation_clazz_;
193 }
194
195 const std::string& GinJavaMethodInvocationHelper::GetErrorMessage() {
196 return error_message_;
197 }
198
199 void GinJavaMethodInvocationHelper::InvokeMethod(jobject object,
200 const JavaType& return_type,
201 jmethodID id,
202 jvalue* parameters) {
203 JNIEnv* env = AttachCurrentThread();
204 base::ListValue result_wrapper;
205 switch (return_type.type) {
206 case JavaType::TypeBoolean:
207 result_wrapper.AppendBoolean(
208 env->CallBooleanMethodA(object, id, parameters));
209 break;
210 case JavaType::TypeByte:
211 result_wrapper.AppendInteger(
212 env->CallByteMethodA(object, id, parameters));
213 break;
214 case JavaType::TypeChar:
215 result_wrapper.AppendInteger(
216 env->CallCharMethodA(object, id, parameters));
217 break;
218 case JavaType::TypeShort:
219 result_wrapper.AppendInteger(
220 env->CallShortMethodA(object, id, parameters));
221 break;
222 case JavaType::TypeInt:
223 result_wrapper.AppendInteger(
224 env->CallIntMethodA(object, id, parameters));
225 break;
226 case JavaType::TypeLong:
227 result_wrapper.AppendDouble(
228 env->CallLongMethodA(object, id, parameters));
229 break;
230 case JavaType::TypeFloat: {
231 float result = env->CallFloatMethodA(object, id, parameters);
232 if (base::IsFinite(result)) {
233 result_wrapper.AppendDouble(result);
234 } else {
235 result_wrapper.Append(
236 GinJavaBridgeValue::CreateNonFiniteValue(result).release());
237 }
238 break;
239 }
240 case JavaType::TypeDouble: {
241 double result = env->CallDoubleMethodA(object, id, parameters);
242 if (base::IsFinite(result)) {
243 result_wrapper.AppendDouble(result);
244 } else {
245 result_wrapper.Append(
246 GinJavaBridgeValue::CreateNonFiniteValue(result).release());
247 }
248 break;
249 }
250 case JavaType::TypeVoid:
251 env->CallVoidMethodA(object, id, parameters);
252 result_wrapper.Append(
253 GinJavaBridgeValue::CreateUndefinedValue().release());
254 break;
255 case JavaType::TypeArray:
256 // LIVECONNECT_COMPLIANCE: Existing behavior is to not call methods that
257 // return arrays. Spec requires calling the method and converting the
258 // result to a JavaScript array.
259 result_wrapper.Append(
260 GinJavaBridgeValue::CreateUndefinedValue().release());
261 break;
262 case JavaType::TypeString: {
263 jstring java_string = static_cast<jstring>(
264 env->CallObjectMethodA(object, id, parameters));
265 // If an exception was raised, we must clear it before calling most JNI
266 // methods. ScopedJavaLocalRef is liable to make such calls, so we test
267 // first.
268 if (base::android::ClearException(env)) {
269 SetInvocationFailure(kJavaExceptionRaised);
270 return;
271 }
272 ScopedJavaLocalRef<jstring> scoped_java_string(env, java_string);
273 if (!scoped_java_string.obj()) {
274 // LIVECONNECT_COMPLIANCE: Existing behavior is to return undefined.
275 // Spec requires returning a null string.
276 result_wrapper.Append(
277 GinJavaBridgeValue::CreateUndefinedValue().release());
278 break;
279 }
280 result_wrapper.AppendString(
281 base::android::ConvertJavaStringToUTF8(scoped_java_string));
282 break;
283 }
284 case JavaType::TypeObject: {
285 // If an exception was raised, we must clear it before calling most JNI
286 // methods. ScopedJavaLocalRef is liable to make such calls, so we test
287 // first.
288 jobject java_object = env->CallObjectMethodA(object, id, parameters);
289 if (base::android::ClearException(env)) {
290 SetInvocationFailure(kJavaExceptionRaised);
291 return;
292 }
293 ScopedJavaLocalRef<jobject> scoped_java_object(env, java_object);
294 if (!scoped_java_object.obj()) {
295 result_wrapper.Append(base::Value::CreateNullValue());
296 break;
297 }
298 SetObjectResult(scoped_java_object, object_->GetSafeAnnotationClass());
299 return;
300 }
301 }
302 // This is for all cases except JavaType::TypeObject.
303 if (!base::android::ClearException(env)) {
304 SetPrimitiveResult(result_wrapper);
305 } else {
306 SetInvocationFailure(kJavaExceptionRaised);
307 }
308 }
309
310 } // namespace content
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698