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