• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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/android/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/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"
18 
19 using base::android::AttachCurrentThread;
20 using base::android::ScopedJavaLocalRef;
21 
22 namespace content {
23 
24 namespace {
25 
26 // See frameworks/base/core/java/android/webkit/EventLogTags.logtags
27 const int kObjectGetClassInvocationAttemptLogTag = 70151;
28 
29 // This is an intermediate solution until we fix http://crbug.com/391492.
ConvertJavaStringToUTF8(JNIEnv * env,jstring str)30 std::string ConvertJavaStringToUTF8(JNIEnv* env, jstring str) {
31   const jchar* chars = env->GetStringChars(str, NULL);
32   DCHECK(chars);
33   blink::WebString utf16(chars, env->GetStringLength(str));
34   env->ReleaseStringChars(str, chars);
35   return utf16.utf8();
36 }
37 
38 }  // namespace
39 
GinJavaMethodInvocationHelper(scoped_ptr<ObjectDelegate> object,const std::string & method_name,const base::ListValue & arguments)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) {
48 }
49 
~GinJavaMethodInvocationHelper()50 GinJavaMethodInvocationHelper::~GinJavaMethodInvocationHelper() {}
51 
Init(DispatcherDelegate * dispatcher)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());
57 }
58 
59 // As V8ValueConverter has finite recursion depth when serializing
60 // JavaScript values, we don't bother about having a recursion threshold here.
BuildObjectRefsFromListValue(DispatcherDelegate * dispatcher,const base::Value * list_value)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();
68        iter != list->end();
69        ++iter) {
70     if (AppendObjectRef(dispatcher, *iter))
71       continue;
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);
76     }
77   }
78 }
79 
BuildObjectRefsFromDictionaryValue(DispatcherDelegate * dispatcher,const base::Value * dict_value)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);
87        !iter.IsAtEnd();
88        iter.Advance()) {
89     if (AppendObjectRef(dispatcher, &iter.value()))
90       continue;
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());
95     }
96   }
97 }
98 
AppendObjectRef(DispatcherDelegate * dispatcher,const base::Value * raw_value)99 bool GinJavaMethodInvocationHelper::AppendObjectRef(
100     DispatcherDelegate* dispatcher,
101     const base::Value* raw_value) {
102   if (!GinJavaBridgeValue::ContainsGinJavaBridgeValue(raw_value))
103     return false;
104   scoped_ptr<const GinJavaBridgeValue> value(
105       GinJavaBridgeValue::FromValue(raw_value));
106   if (!value->IsType(GinJavaBridgeValue::TYPE_OBJECT_ID))
107     return false;
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));
116       }
117     }
118   }
119   return true;
120 }
121 
Invoke()122 void GinJavaMethodInvocationHelper::Invoke() {
123   JNIEnv* env = AttachCurrentThread();
124   const JavaMethod* method =
125       object_->FindMethod(method_name_, arguments_->GetSize());
126   if (!method) {
127     SetInvocationError(kGinJavaBridgeMethodNotFound);
128     return;
129   }
130 
131   if (object_->IsObjectGetClassMethod(method)) {
132     base::android::EventLogWriteInt(kObjectGetClassInvocationAttemptLogTag,
133                                     getuid());
134     SetInvocationError(kGinJavaBridgeAccessToObjectGetClassIsBlocked);
135     return;
136   }
137 
138   ScopedJavaLocalRef<jobject> obj;
139   ScopedJavaLocalRef<jclass> cls;
140   if (method->is_static()) {
141     cls = object_->GetLocalClassRef(env);
142   } else {
143     obj = object_->GetLocalRef(env);
144   }
145   if (obj.is_null() && cls.is_null()) {
146     SetInvocationError(kGinJavaBridgeObjectIsGone);
147     return;
148   }
149 
150   std::vector<jvalue> parameters(method->num_parameters());
151   for (size_t i = 0; i < method->num_parameters(); ++i) {
152     const base::Value* argument;
153     arguments_->Get(i, &argument);
154     parameters[i] = CoerceJavaScriptValueToJavaValue(
155         env, argument, method->parameter_type(i), true, object_refs_);
156   }
157   if (method->is_static()) {
158     InvokeMethod(
159         NULL, cls.obj(), method->return_type(), method->id(), &parameters[0]);
160   } else {
161     InvokeMethod(
162         obj.obj(), NULL, method->return_type(), method->id(), &parameters[0]);
163   }
164 
165   // Now that we're done with the jvalue, release any local references created
166   // by CoerceJavaScriptValueToJavaValue().
167   for (size_t i = 0; i < method->num_parameters(); ++i) {
168     ReleaseJavaValueIfRequired(env, &parameters[i], method->parameter_type(i));
169   }
170 }
171 
SetInvocationError(GinJavaBridgeError error)172 void GinJavaMethodInvocationHelper::SetInvocationError(
173     GinJavaBridgeError error) {
174   holds_primitive_result_ = true;
175   primitive_result_.reset(new base::ListValue());
176   invocation_error_ = error;
177 }
178 
SetPrimitiveResult(const base::ListValue & result_wrapper)179 void GinJavaMethodInvocationHelper::SetPrimitiveResult(
180     const base::ListValue& result_wrapper) {
181   holds_primitive_result_ = true;
182   primitive_result_.reset(result_wrapper.DeepCopy());
183 }
184 
SetObjectResult(const base::android::JavaRef<jobject> & object,const base::android::JavaRef<jclass> & safe_annotation_clazz)185 void GinJavaMethodInvocationHelper::SetObjectResult(
186     const base::android::JavaRef<jobject>& object,
187     const base::android::JavaRef<jclass>& safe_annotation_clazz) {
188   holds_primitive_result_ = false;
189   object_result_.Reset(object);
190   safe_annotation_clazz_.Reset(safe_annotation_clazz);
191 }
192 
HoldsPrimitiveResult()193 bool GinJavaMethodInvocationHelper::HoldsPrimitiveResult() {
194   return holds_primitive_result_;
195 }
196 
GetPrimitiveResult()197 const base::ListValue& GinJavaMethodInvocationHelper::GetPrimitiveResult() {
198   return *primitive_result_.get();
199 }
200 
201 const base::android::JavaRef<jobject>&
GetObjectResult()202 GinJavaMethodInvocationHelper::GetObjectResult() {
203   return object_result_;
204 }
205 
206 const base::android::JavaRef<jclass>&
GetSafeAnnotationClass()207 GinJavaMethodInvocationHelper::GetSafeAnnotationClass() {
208   return safe_annotation_clazz_;
209 }
210 
GetInvocationError()211 const GinJavaBridgeError GinJavaMethodInvocationHelper::GetInvocationError() {
212   return invocation_error_;
213 }
214 
InvokeMethod(jobject object,jclass clazz,const JavaType & return_type,jmethodID id,jvalue * parameters)215 void GinJavaMethodInvocationHelper::InvokeMethod(jobject object,
216                                                  jclass clazz,
217                                                  const JavaType& return_type,
218                                                  jmethodID id,
219                                                  jvalue* parameters) {
220   DCHECK(object || clazz);
221   JNIEnv* env = AttachCurrentThread();
222   base::ListValue result_wrapper;
223   switch (return_type.type) {
224     case JavaType::TypeBoolean:
225       result_wrapper.AppendBoolean(
226           object ? env->CallBooleanMethodA(object, id, parameters)
227                  : env->CallStaticBooleanMethodA(clazz, id, parameters));
228       break;
229     case JavaType::TypeByte:
230       result_wrapper.AppendInteger(
231           object ? env->CallByteMethodA(object, id, parameters)
232                  : env->CallStaticByteMethodA(clazz, id, parameters));
233       break;
234     case JavaType::TypeChar:
235       result_wrapper.AppendInteger(
236           object ? env->CallCharMethodA(object, id, parameters)
237                  : env->CallStaticCharMethodA(clazz, id, parameters));
238       break;
239     case JavaType::TypeShort:
240       result_wrapper.AppendInteger(
241           object ? env->CallShortMethodA(object, id, parameters)
242                  : env->CallStaticShortMethodA(clazz, id, parameters));
243       break;
244     case JavaType::TypeInt:
245       result_wrapper.AppendInteger(
246           object ? env->CallIntMethodA(object, id, parameters)
247                  : env->CallStaticIntMethodA(clazz, id, parameters));
248       break;
249     case JavaType::TypeLong:
250       result_wrapper.AppendDouble(
251           object ? env->CallLongMethodA(object, id, parameters)
252                  : env->CallStaticLongMethodA(clazz, id, parameters));
253       break;
254     case JavaType::TypeFloat: {
255       float result = object
256                          ? env->CallFloatMethodA(object, id, parameters)
257                          : env->CallStaticFloatMethodA(clazz, id, parameters);
258       if (base::IsFinite(result)) {
259         result_wrapper.AppendDouble(result);
260       } else {
261         result_wrapper.Append(
262             GinJavaBridgeValue::CreateNonFiniteValue(result).release());
263       }
264       break;
265     }
266     case JavaType::TypeDouble: {
267       double result = object
268                           ? env->CallDoubleMethodA(object, id, parameters)
269                           : env->CallStaticDoubleMethodA(clazz, id, parameters);
270       if (base::IsFinite(result)) {
271         result_wrapper.AppendDouble(result);
272       } else {
273         result_wrapper.Append(
274             GinJavaBridgeValue::CreateNonFiniteValue(result).release());
275       }
276       break;
277     }
278     case JavaType::TypeVoid:
279       if (object)
280         env->CallVoidMethodA(object, id, parameters);
281       else
282         env->CallStaticVoidMethodA(clazz, id, parameters);
283       result_wrapper.Append(
284           GinJavaBridgeValue::CreateUndefinedValue().release());
285       break;
286     case JavaType::TypeArray:
287       // LIVECONNECT_COMPLIANCE: Existing behavior is to not call methods that
288       // return arrays. Spec requires calling the method and converting the
289       // result to a JavaScript array.
290       result_wrapper.Append(
291           GinJavaBridgeValue::CreateUndefinedValue().release());
292       break;
293     case JavaType::TypeString: {
294       jstring java_string = static_cast<jstring>(
295           object ? env->CallObjectMethodA(object, id, parameters)
296                  : env->CallStaticObjectMethodA(clazz, id, parameters));
297       // If an exception was raised, we must clear it before calling most JNI
298       // methods. ScopedJavaLocalRef is liable to make such calls, so we test
299       // first.
300       if (base::android::ClearException(env)) {
301         SetInvocationError(kGinJavaBridgeJavaExceptionRaised);
302         return;
303       }
304       ScopedJavaLocalRef<jstring> scoped_java_string(env, java_string);
305       if (!scoped_java_string.obj()) {
306         // LIVECONNECT_COMPLIANCE: Existing behavior is to return undefined.
307         // Spec requires returning a null string.
308         result_wrapper.Append(
309             GinJavaBridgeValue::CreateUndefinedValue().release());
310         break;
311       }
312       result_wrapper.AppendString(
313           ConvertJavaStringToUTF8(env, scoped_java_string.obj()));
314       break;
315     }
316     case JavaType::TypeObject: {
317       // If an exception was raised, we must clear it before calling most JNI
318       // methods. ScopedJavaLocalRef is liable to make such calls, so we test
319       // first.
320       jobject java_object =
321           object ? env->CallObjectMethodA(object, id, parameters)
322                  : env->CallStaticObjectMethodA(clazz, id, parameters);
323       if (base::android::ClearException(env)) {
324         SetInvocationError(kGinJavaBridgeJavaExceptionRaised);
325         return;
326       }
327       ScopedJavaLocalRef<jobject> scoped_java_object(env, java_object);
328       if (!scoped_java_object.obj()) {
329         result_wrapper.Append(base::Value::CreateNullValue());
330         break;
331       }
332       SetObjectResult(scoped_java_object, object_->GetSafeAnnotationClass());
333       return;
334     }
335   }
336   // This is for all cases except JavaType::TypeObject.
337   if (!base::android::ClearException(env)) {
338     SetPrimitiveResult(result_wrapper);
339   } else {
340     SetInvocationError(kGinJavaBridgeJavaExceptionRaised);
341   }
342 }
343 
344 }  // namespace content
345