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