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   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,
    156                                                      argument,
    157                                                      method->parameter_type(i),
    158                                                      true,
    159                                                      object_refs_,
    160                                                      &coercion_error);
    161   }
    162 
    163   if (coercion_error == kGinJavaBridgeNoError) {
    164     if (method->is_static()) {
    165       InvokeMethod(
    166           NULL, cls.obj(), method->return_type(), method->id(), &parameters[0]);
    167     } else {
    168       InvokeMethod(
    169           obj.obj(), NULL, method->return_type(), method->id(), &parameters[0]);
    170     }
    171   } else {
    172     SetInvocationError(coercion_error);
    173   }
    174 
    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, &parameters[i], method->parameter_type(i));
    179   }
    180 }
    181 
    182 void GinJavaMethodInvocationHelper::SetInvocationError(
    183     GinJavaBridgeError error) {
    184   holds_primitive_result_ = true;
    185   primitive_result_.reset(new base::ListValue());
    186   invocation_error_ = error;
    187 }
    188 
    189 void GinJavaMethodInvocationHelper::SetPrimitiveResult(
    190     const base::ListValue& result_wrapper) {
    191   holds_primitive_result_ = true;
    192   primitive_result_.reset(result_wrapper.DeepCopy());
    193 }
    194 
    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);
    201 }
    202 
    203 bool GinJavaMethodInvocationHelper::HoldsPrimitiveResult() {
    204   return holds_primitive_result_;
    205 }
    206 
    207 const base::ListValue& GinJavaMethodInvocationHelper::GetPrimitiveResult() {
    208   return *primitive_result_.get();
    209 }
    210 
    211 const base::android::JavaRef<jobject>&
    212 GinJavaMethodInvocationHelper::GetObjectResult() {
    213   return object_result_;
    214 }
    215 
    216 const base::android::JavaRef<jclass>&
    217 GinJavaMethodInvocationHelper::GetSafeAnnotationClass() {
    218   return safe_annotation_clazz_;
    219 }
    220 
    221 const GinJavaBridgeError GinJavaMethodInvocationHelper::GetInvocationError() {
    222   return invocation_error_;
    223 }
    224 
    225 void GinJavaMethodInvocationHelper::InvokeMethod(jobject object,
    226                                                  jclass clazz,
    227                                                  const JavaType& return_type,
    228                                                  jmethodID id,
    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));
    238       break;
    239     case JavaType::TypeByte:
    240       result_wrapper.AppendInteger(
    241           object ? env->CallByteMethodA(object, id, parameters)
    242                  : env->CallStaticByteMethodA(clazz, id, parameters));
    243       break;
    244     case JavaType::TypeChar:
    245       result_wrapper.AppendInteger(
    246           object ? env->CallCharMethodA(object, id, parameters)
    247                  : env->CallStaticCharMethodA(clazz, id, parameters));
    248       break;
    249     case JavaType::TypeShort:
    250       result_wrapper.AppendInteger(
    251           object ? env->CallShortMethodA(object, id, parameters)
    252                  : env->CallStaticShortMethodA(clazz, id, parameters));
    253       break;
    254     case JavaType::TypeInt:
    255       result_wrapper.AppendInteger(
    256           object ? env->CallIntMethodA(object, id, parameters)
    257                  : env->CallStaticIntMethodA(clazz, id, parameters));
    258       break;
    259     case JavaType::TypeLong:
    260       result_wrapper.AppendDouble(
    261           object ? env->CallLongMethodA(object, id, parameters)
    262                  : env->CallStaticLongMethodA(clazz, id, parameters));
    263       break;
    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);
    270       } else {
    271         result_wrapper.Append(
    272             GinJavaBridgeValue::CreateNonFiniteValue(result).release());
    273       }
    274       break;
    275     }
    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);
    282       } else {
    283         result_wrapper.Append(
    284             GinJavaBridgeValue::CreateNonFiniteValue(result).release());
    285       }
    286       break;
    287     }
    288     case JavaType::TypeVoid:
    289       if (object)
    290         env->CallVoidMethodA(object, id, parameters);
    291       else
    292         env->CallStaticVoidMethodA(clazz, id, parameters);
    293       result_wrapper.Append(
    294           GinJavaBridgeValue::CreateUndefinedValue().release());
    295       break;
    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());
    302       break;
    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
    309       // first.
    310       if (base::android::ClearException(env)) {
    311         SetInvocationError(kGinJavaBridgeJavaExceptionRaised);
    312         return;
    313       }
    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());
    320         break;
    321       }
    322       result_wrapper.AppendString(
    323           ConvertJavaStringToUTF8(env, scoped_java_string.obj()));
    324       break;
    325     }
    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
    329       // first.
    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);
    335         return;
    336       }
    337       ScopedJavaLocalRef<jobject> scoped_java_object(env, java_object);
    338       if (!scoped_java_object.obj()) {
    339         result_wrapper.Append(base::Value::CreateNullValue());
    340         break;
    341       }
    342       SetObjectResult(scoped_java_object, object_->GetSafeAnnotationClass());
    343       return;
    344     }
    345   }
    346   // This is for all cases except JavaType::TypeObject.
    347   if (!base::android::ClearException(env)) {
    348     SetPrimitiveResult(result_wrapper);
    349   } else {
    350     SetInvocationError(kGinJavaBridgeJavaExceptionRaised);
    351   }
    352 }
    353 
    354 }  // namespace content
    355