Home | History | Annotate | Download | only in java
      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.
     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 
     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 
     50 GinJavaMethodInvocationHelper::~GinJavaMethodInvocationHelper() {}
     51 
     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.
     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 
     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 
     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 
    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 
    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 
    179 void GinJavaMethodInvocationHelper::SetPrimitiveResult(
    180     const base::ListValue& result_wrapper) {
    181   holds_primitive_result_ = true;
    182   primitive_result_.reset(result_wrapper.DeepCopy());
    183 }
    184 
    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 
    193 bool GinJavaMethodInvocationHelper::HoldsPrimitiveResult() {
    194   return holds_primitive_result_;
    195 }
    196 
    197 const base::ListValue& GinJavaMethodInvocationHelper::GetPrimitiveResult() {
    198   return *primitive_result_.get();
    199 }
    200 
    201 const base::android::JavaRef<jobject>&
    202 GinJavaMethodInvocationHelper::GetObjectResult() {
    203   return object_result_;
    204 }
    205 
    206 const base::android::JavaRef<jclass>&
    207 GinJavaMethodInvocationHelper::GetSafeAnnotationClass() {
    208   return safe_annotation_clazz_;
    209 }
    210 
    211 const GinJavaBridgeError GinJavaMethodInvocationHelper::GetInvocationError() {
    212   return invocation_error_;
    213 }
    214 
    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