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