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(), ¶meters[0]); 167 } else { 168 InvokeMethod( 169 obj.obj(), NULL, method->return_type(), method->id(), ¶meters[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, ¶meters[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