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 "base/android/jni_android.h" 8 #include "content/browser/android/java/jni_helper.h" 9 #include "content/common/android/gin_java_bridge_value.h" 10 #include "testing/gtest/include/gtest/gtest.h" 11 12 namespace content { 13 14 namespace { 15 16 class NullObjectDelegate 17 : public GinJavaMethodInvocationHelper::ObjectDelegate { 18 public: 19 NullObjectDelegate() {} 20 21 virtual ~NullObjectDelegate() {} 22 23 virtual base::android::ScopedJavaLocalRef<jobject> GetLocalRef( 24 JNIEnv* env) OVERRIDE { 25 return base::android::ScopedJavaLocalRef<jobject>(); 26 } 27 28 virtual base::android::ScopedJavaLocalRef<jclass> GetLocalClassRef( 29 JNIEnv* env) OVERRIDE { 30 return base::android::ScopedJavaLocalRef<jclass>(); 31 } 32 33 virtual const JavaMethod* FindMethod(const std::string& method_name, 34 size_t num_parameters) OVERRIDE { 35 return NULL; 36 } 37 38 virtual bool IsObjectGetClassMethod(const JavaMethod* method) OVERRIDE { 39 return false; 40 } 41 42 virtual const base::android::JavaRef<jclass>& GetSafeAnnotationClass() 43 OVERRIDE { 44 return safe_annotation_class_; 45 } 46 47 private: 48 base::android::ScopedJavaLocalRef<jclass> safe_annotation_class_; 49 50 DISALLOW_COPY_AND_ASSIGN(NullObjectDelegate); 51 }; 52 53 class NullDispatcherDelegate 54 : public GinJavaMethodInvocationHelper::DispatcherDelegate { 55 public: 56 NullDispatcherDelegate() {} 57 58 virtual ~NullDispatcherDelegate() {} 59 60 virtual JavaObjectWeakGlobalRef GetObjectWeakRef( 61 GinJavaBoundObject::ObjectID object_id) OVERRIDE { 62 return JavaObjectWeakGlobalRef(); 63 } 64 65 DISALLOW_COPY_AND_ASSIGN(NullDispatcherDelegate); 66 }; 67 68 } // namespace 69 70 class GinJavaMethodInvocationHelperTest : public testing::Test { 71 }; 72 73 namespace { 74 75 class CountingDispatcherDelegate 76 : public GinJavaMethodInvocationHelper::DispatcherDelegate { 77 public: 78 CountingDispatcherDelegate() {} 79 80 virtual ~CountingDispatcherDelegate() {} 81 82 virtual JavaObjectWeakGlobalRef GetObjectWeakRef( 83 GinJavaBoundObject::ObjectID object_id) OVERRIDE { 84 counters_[object_id]++; 85 return JavaObjectWeakGlobalRef(); 86 } 87 88 void AssertInvocationsCount(GinJavaBoundObject::ObjectID begin_object_id, 89 GinJavaBoundObject::ObjectID end_object_id) { 90 EXPECT_EQ(end_object_id - begin_object_id, 91 static_cast<int>(counters_.size())); 92 for (GinJavaBoundObject::ObjectID i = begin_object_id; 93 i < end_object_id; ++i) { 94 EXPECT_LT(0, counters_[i]) << "ObjectID: " << i; 95 } 96 } 97 98 private: 99 typedef std::map<GinJavaBoundObject::ObjectID, int> Counters; 100 Counters counters_; 101 102 DISALLOW_COPY_AND_ASSIGN(CountingDispatcherDelegate); 103 }; 104 105 } // namespace 106 107 TEST_F(GinJavaMethodInvocationHelperTest, RetrievalOfObjectsNoObjects) { 108 base::ListValue no_objects; 109 for (int i = 0; i < 10; ++i) { 110 no_objects.AppendInteger(i); 111 } 112 113 scoped_refptr<GinJavaMethodInvocationHelper> helper = 114 new GinJavaMethodInvocationHelper( 115 scoped_ptr<GinJavaMethodInvocationHelper::ObjectDelegate>( 116 new NullObjectDelegate()), 117 "foo", 118 no_objects); 119 CountingDispatcherDelegate counter; 120 helper->Init(&counter); 121 counter.AssertInvocationsCount(0, 0); 122 } 123 124 TEST_F(GinJavaMethodInvocationHelperTest, RetrievalOfObjectsHaveObjects) { 125 base::ListValue objects; 126 objects.AppendInteger(100); 127 objects.Append(GinJavaBridgeValue::CreateObjectIDValue(1).release()); 128 base::ListValue* sub_list = new base::ListValue(); 129 sub_list->AppendInteger(200); 130 sub_list->Append(GinJavaBridgeValue::CreateObjectIDValue(2).release()); 131 objects.Append(sub_list); 132 base::DictionaryValue* sub_dict = new base::DictionaryValue(); 133 sub_dict->SetInteger("1", 300); 134 sub_dict->Set("2", GinJavaBridgeValue::CreateObjectIDValue(3).release()); 135 objects.Append(sub_dict); 136 base::ListValue* sub_list_with_dict = new base::ListValue(); 137 base::DictionaryValue* sub_sub_dict = new base::DictionaryValue(); 138 sub_sub_dict->Set("1", GinJavaBridgeValue::CreateObjectIDValue(4).release()); 139 sub_list_with_dict->Append(sub_sub_dict); 140 objects.Append(sub_list_with_dict); 141 base::DictionaryValue* sub_dict_with_list = new base::DictionaryValue(); 142 base::ListValue* sub_sub_list = new base::ListValue(); 143 sub_sub_list->Append(GinJavaBridgeValue::CreateObjectIDValue(5).release()); 144 sub_dict_with_list->Set("1", sub_sub_list); 145 objects.Append(sub_dict_with_list); 146 147 scoped_refptr<GinJavaMethodInvocationHelper> helper = 148 new GinJavaMethodInvocationHelper( 149 scoped_ptr<GinJavaMethodInvocationHelper::ObjectDelegate>( 150 new NullObjectDelegate()), 151 "foo", 152 objects); 153 CountingDispatcherDelegate counter; 154 helper->Init(&counter); 155 counter.AssertInvocationsCount(1, 6); 156 } 157 158 namespace { 159 160 class ObjectIsGoneObjectDelegate : public NullObjectDelegate { 161 public: 162 ObjectIsGoneObjectDelegate() : 163 get_local_ref_called_(false) { 164 // We need a Java Method object to create a valid JavaMethod instance. 165 JNIEnv* env = base::android::AttachCurrentThread(); 166 jmethodID method_id = 167 GetMethodIDFromClassName(env, "java/lang/Object", "hashCode", "()I"); 168 EXPECT_TRUE(method_id); 169 base::android::ScopedJavaLocalRef<jobject> method_obj( 170 env, 171 env->ToReflectedMethod( 172 base::android::GetClass(env, "java/lang/Object").obj(), 173 method_id, 174 false)); 175 EXPECT_TRUE(method_obj.obj()); 176 method_.reset(new JavaMethod(method_obj)); 177 } 178 179 virtual ~ObjectIsGoneObjectDelegate() {} 180 181 virtual base::android::ScopedJavaLocalRef<jobject> GetLocalRef( 182 JNIEnv* env) OVERRIDE { 183 get_local_ref_called_ = true; 184 return NullObjectDelegate::GetLocalRef(env); 185 } 186 187 virtual const JavaMethod* FindMethod(const std::string& method_name, 188 size_t num_parameters) OVERRIDE { 189 return method_.get(); 190 } 191 192 bool get_local_ref_called() { return get_local_ref_called_; } 193 194 const std::string& get_method_name() { return method_->name(); } 195 196 protected: 197 scoped_ptr<JavaMethod> method_; 198 bool get_local_ref_called_; 199 200 private: 201 DISALLOW_COPY_AND_ASSIGN(ObjectIsGoneObjectDelegate); 202 }; 203 204 } // namespace 205 206 TEST_F(GinJavaMethodInvocationHelperTest, HandleObjectIsGone) { 207 base::ListValue no_objects; 208 ObjectIsGoneObjectDelegate* object_delegate = 209 new ObjectIsGoneObjectDelegate(); 210 scoped_refptr<GinJavaMethodInvocationHelper> helper = 211 new GinJavaMethodInvocationHelper( 212 scoped_ptr<GinJavaMethodInvocationHelper::ObjectDelegate>( 213 object_delegate), 214 object_delegate->get_method_name(), 215 no_objects); 216 NullDispatcherDelegate dispatcher; 217 helper->Init(&dispatcher); 218 EXPECT_FALSE(object_delegate->get_local_ref_called()); 219 EXPECT_EQ(kGinJavaBridgeNoError, helper->GetInvocationError()); 220 helper->Invoke(); 221 EXPECT_TRUE(object_delegate->get_local_ref_called()); 222 EXPECT_TRUE(helper->HoldsPrimitiveResult()); 223 EXPECT_TRUE(helper->GetPrimitiveResult().empty()); 224 EXPECT_EQ(kGinJavaBridgeObjectIsGone, helper->GetInvocationError()); 225 } 226 227 namespace { 228 229 class MethodNotFoundObjectDelegate : public NullObjectDelegate { 230 public: 231 MethodNotFoundObjectDelegate() : find_method_called_(false) {} 232 233 virtual ~MethodNotFoundObjectDelegate() {} 234 235 virtual base::android::ScopedJavaLocalRef<jobject> GetLocalRef( 236 JNIEnv* env) OVERRIDE { 237 return base::android::ScopedJavaLocalRef<jobject>( 238 env, static_cast<jobject>(env->FindClass("java/lang/String"))); 239 } 240 241 virtual const JavaMethod* FindMethod(const std::string& method_name, 242 size_t num_parameters) OVERRIDE { 243 find_method_called_ = true; 244 return NULL; 245 } 246 247 bool find_method_called() const { return find_method_called_; } 248 249 protected: 250 bool find_method_called_; 251 252 private: 253 DISALLOW_COPY_AND_ASSIGN(MethodNotFoundObjectDelegate); 254 }; 255 256 } // namespace 257 258 TEST_F(GinJavaMethodInvocationHelperTest, HandleMethodNotFound) { 259 base::ListValue no_objects; 260 MethodNotFoundObjectDelegate* object_delegate = 261 new MethodNotFoundObjectDelegate(); 262 scoped_refptr<GinJavaMethodInvocationHelper> helper = 263 new GinJavaMethodInvocationHelper( 264 scoped_ptr<GinJavaMethodInvocationHelper::ObjectDelegate>( 265 object_delegate), 266 "foo", 267 no_objects); 268 NullDispatcherDelegate dispatcher; 269 helper->Init(&dispatcher); 270 EXPECT_FALSE(object_delegate->find_method_called()); 271 EXPECT_EQ(kGinJavaBridgeNoError, helper->GetInvocationError()); 272 helper->Invoke(); 273 EXPECT_TRUE(object_delegate->find_method_called()); 274 EXPECT_TRUE(helper->HoldsPrimitiveResult()); 275 EXPECT_TRUE(helper->GetPrimitiveResult().empty()); 276 EXPECT_EQ(kGinJavaBridgeMethodNotFound, helper->GetInvocationError()); 277 } 278 279 namespace { 280 281 class GetClassObjectDelegate : public MethodNotFoundObjectDelegate { 282 public: 283 GetClassObjectDelegate() : get_class_called_(false) {} 284 285 virtual ~GetClassObjectDelegate() {} 286 287 virtual const JavaMethod* FindMethod(const std::string& method_name, 288 size_t num_parameters) OVERRIDE { 289 find_method_called_ = true; 290 return kFakeGetClass; 291 } 292 293 virtual bool IsObjectGetClassMethod(const JavaMethod* method) OVERRIDE { 294 get_class_called_ = true; 295 return kFakeGetClass == method; 296 } 297 298 bool get_class_called() const { return get_class_called_; } 299 300 private: 301 static const JavaMethod* kFakeGetClass; 302 bool get_class_called_; 303 304 DISALLOW_COPY_AND_ASSIGN(GetClassObjectDelegate); 305 }; 306 307 // We don't expect GinJavaMethodInvocationHelper to actually invoke the 308 // method, since the point of the test is to verify whether calls to 309 // 'getClass' get blocked. 310 const JavaMethod* GetClassObjectDelegate::kFakeGetClass = 311 (JavaMethod*)0xdeadbeef; 312 313 } // namespace 314 315 TEST_F(GinJavaMethodInvocationHelperTest, HandleGetClassInvocation) { 316 base::ListValue no_objects; 317 GetClassObjectDelegate* object_delegate = 318 new GetClassObjectDelegate(); 319 scoped_refptr<GinJavaMethodInvocationHelper> helper = 320 new GinJavaMethodInvocationHelper( 321 scoped_ptr<GinJavaMethodInvocationHelper::ObjectDelegate>( 322 object_delegate), 323 "foo", 324 no_objects); 325 NullDispatcherDelegate dispatcher; 326 helper->Init(&dispatcher); 327 EXPECT_FALSE(object_delegate->find_method_called()); 328 EXPECT_FALSE(object_delegate->get_class_called()); 329 EXPECT_EQ(kGinJavaBridgeNoError, helper->GetInvocationError()); 330 helper->Invoke(); 331 EXPECT_TRUE(object_delegate->find_method_called()); 332 EXPECT_TRUE(object_delegate->get_class_called()); 333 EXPECT_TRUE(helper->HoldsPrimitiveResult()); 334 EXPECT_TRUE(helper->GetPrimitiveResult().empty()); 335 EXPECT_EQ(kGinJavaBridgeAccessToObjectGetClassIsBlocked, 336 helper->GetInvocationError()); 337 } 338 339 } // namespace content 340