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:
NullObjectDelegate()19 NullObjectDelegate() {}
20
~NullObjectDelegate()21 virtual ~NullObjectDelegate() {}
22
GetLocalRef(JNIEnv * env)23 virtual base::android::ScopedJavaLocalRef<jobject> GetLocalRef(
24 JNIEnv* env) OVERRIDE {
25 return base::android::ScopedJavaLocalRef<jobject>();
26 }
27
GetLocalClassRef(JNIEnv * env)28 virtual base::android::ScopedJavaLocalRef<jclass> GetLocalClassRef(
29 JNIEnv* env) OVERRIDE {
30 return base::android::ScopedJavaLocalRef<jclass>();
31 }
32
FindMethod(const std::string & method_name,size_t num_parameters)33 virtual const JavaMethod* FindMethod(const std::string& method_name,
34 size_t num_parameters) OVERRIDE {
35 return NULL;
36 }
37
IsObjectGetClassMethod(const JavaMethod * method)38 virtual bool IsObjectGetClassMethod(const JavaMethod* method) OVERRIDE {
39 return false;
40 }
41
GetSafeAnnotationClass()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:
NullDispatcherDelegate()56 NullDispatcherDelegate() {}
57
~NullDispatcherDelegate()58 virtual ~NullDispatcherDelegate() {}
59
GetObjectWeakRef(GinJavaBoundObject::ObjectID object_id)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:
CountingDispatcherDelegate()78 CountingDispatcherDelegate() {}
79
~CountingDispatcherDelegate()80 virtual ~CountingDispatcherDelegate() {}
81
GetObjectWeakRef(GinJavaBoundObject::ObjectID object_id)82 virtual JavaObjectWeakGlobalRef GetObjectWeakRef(
83 GinJavaBoundObject::ObjectID object_id) OVERRIDE {
84 counters_[object_id]++;
85 return JavaObjectWeakGlobalRef();
86 }
87
AssertInvocationsCount(GinJavaBoundObject::ObjectID begin_object_id,GinJavaBoundObject::ObjectID end_object_id)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
TEST_F(GinJavaMethodInvocationHelperTest,RetrievalOfObjectsNoObjects)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
TEST_F(GinJavaMethodInvocationHelperTest,RetrievalOfObjectsHaveObjects)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:
ObjectIsGoneObjectDelegate()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
~ObjectIsGoneObjectDelegate()179 virtual ~ObjectIsGoneObjectDelegate() {}
180
GetLocalRef(JNIEnv * env)181 virtual base::android::ScopedJavaLocalRef<jobject> GetLocalRef(
182 JNIEnv* env) OVERRIDE {
183 get_local_ref_called_ = true;
184 return NullObjectDelegate::GetLocalRef(env);
185 }
186
FindMethod(const std::string & method_name,size_t num_parameters)187 virtual const JavaMethod* FindMethod(const std::string& method_name,
188 size_t num_parameters) OVERRIDE {
189 return method_.get();
190 }
191
get_local_ref_called()192 bool get_local_ref_called() { return get_local_ref_called_; }
193
get_method_name()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
TEST_F(GinJavaMethodInvocationHelperTest,HandleObjectIsGone)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:
MethodNotFoundObjectDelegate()231 MethodNotFoundObjectDelegate() : find_method_called_(false) {}
232
~MethodNotFoundObjectDelegate()233 virtual ~MethodNotFoundObjectDelegate() {}
234
GetLocalRef(JNIEnv * env)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
FindMethod(const std::string & method_name,size_t num_parameters)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
find_method_called() const247 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
TEST_F(GinJavaMethodInvocationHelperTest,HandleMethodNotFound)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:
GetClassObjectDelegate()283 GetClassObjectDelegate() : get_class_called_(false) {}
284
~GetClassObjectDelegate()285 virtual ~GetClassObjectDelegate() {}
286
FindMethod(const std::string & method_name,size_t num_parameters)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
IsObjectGetClassMethod(const JavaMethod * method)293 virtual bool IsObjectGetClassMethod(const JavaMethod* method) OVERRIDE {
294 get_class_called_ = true;
295 return kFakeGetClass == method;
296 }
297
get_class_called() const298 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
TEST_F(GinJavaMethodInvocationHelperTest,HandleGetClassInvocation)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