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
12namespace content {
13
14namespace {
15
16class 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
53class 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
70class GinJavaMethodInvocationHelperTest : public testing::Test {
71};
72
73namespace {
74
75class 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
107TEST_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
124TEST_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
158namespace {
159
160class 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
206TEST_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
227namespace {
228
229class 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
258TEST_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
279namespace {
280
281class 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.
310const JavaMethod* GetClassObjectDelegate::kFakeGetClass =
311    (JavaMethod*)0xdeadbeef;
312
313}  // namespace
314
315TEST_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