- add sources.
[platform/framework/web/crosswalk.git] / src / ppapi / proxy / plugin_var_tracker_unittest.cc
1 // Copyright (c) 2012 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 "ipc/ipc_test_sink.h"
6 #include "ppapi/c/dev/ppp_class_deprecated.h"
7 #include "ppapi/proxy/plugin_var_tracker.h"
8 #include "ppapi/proxy/ppapi_messages.h"
9 #include "ppapi/proxy/ppapi_proxy_test.h"
10 #include "ppapi/proxy/proxy_object_var.h"
11 #include "ppapi/shared_impl/proxy_lock.h"
12
13 namespace ppapi {
14 namespace proxy {
15
16 namespace {
17
18 PP_Var MakeObject(int32 object_id) {
19   PP_Var ret;
20   ret.type = PP_VARTYPE_OBJECT;
21   ret.value.as_id = object_id;
22   return ret;
23 }
24
25 // A Deallocate() function for PPP_Class that just increments the integer
26 // referenced by the pointer so we know how often Deallocate was called.
27 void MarkOnDeallocate(void* object) {
28   (*static_cast<int*>(object))++;
29 }
30
31 // A class that just implements MarkOnDeallocate on destruction.
32 PPP_Class_Deprecated mark_on_deallocate_class = {
33   NULL,  // HasProperty,
34   NULL,  // HasMethod,
35   NULL,  // GetProperty,
36   NULL,  // GetAllPropertyNames,
37   NULL,  // SetProperty,
38   NULL,  // RemoveProperty,
39   NULL,  // Call,
40   NULL,  // Construct,
41   &MarkOnDeallocate
42 };
43
44 }  // namespace
45
46 class PluginVarTrackerTest : public PluginProxyTest {
47  public:
48   PluginVarTrackerTest() {}
49
50  protected:
51   // Asserts that there is a unique "release object" IPC message in the test
52   // sink. This will return the var ID from the message or -1 if none found.
53   int32 GetObjectIDForUniqueReleaseObject() {
54     const IPC::Message* release_msg = sink().GetUniqueMessageMatching(
55         PpapiHostMsg_PPBVar_ReleaseObject::ID);
56     if (!release_msg)
57       return -1;
58
59     Tuple1<int64> id;
60     PpapiHostMsg_PPBVar_ReleaseObject::Read(release_msg, &id);
61     return id.a;
62   }
63 };
64
65 TEST_F(PluginVarTrackerTest, GetHostObject) {
66   ProxyAutoLock lock;
67   PP_Var host_object = MakeObject(12345);
68
69   // Round-trip through the tracker to make sure the host object comes out the
70   // other end.
71   PP_Var plugin_object = var_tracker().ReceiveObjectPassRef(
72       host_object, plugin_dispatcher());
73   PP_Var host_object2 = var_tracker().GetHostObject(plugin_object);
74   EXPECT_EQ(PP_VARTYPE_OBJECT, host_object2.type);
75   EXPECT_EQ(host_object.value.as_id, host_object2.value.as_id);
76
77   var_tracker().ReleaseVar(plugin_object);
78 }
79
80 TEST_F(PluginVarTrackerTest, ReceiveObjectPassRef) {
81   ProxyAutoLock lock;
82   PP_Var host_object = MakeObject(12345);
83
84   // Receive the object, we should have one ref and no messages.
85   PP_Var plugin_object = var_tracker().ReceiveObjectPassRef(
86       host_object, plugin_dispatcher());
87   EXPECT_EQ(0u, sink().message_count());
88   EXPECT_EQ(1, var_tracker().GetRefCountForObject(plugin_object));
89   EXPECT_EQ(0,
90       var_tracker().GetTrackedWithNoReferenceCountForObject(plugin_object));
91
92   // Receive the same object again, we should get the same plugin ID out.
93   PP_Var plugin_object2 = var_tracker().ReceiveObjectPassRef(
94       host_object, plugin_dispatcher());
95   EXPECT_EQ(plugin_object.value.as_id, plugin_object2.value.as_id);
96   EXPECT_EQ(2, var_tracker().GetRefCountForObject(plugin_object));
97   EXPECT_EQ(0,
98       var_tracker().GetTrackedWithNoReferenceCountForObject(plugin_object));
99
100   // It should have sent one message to decerment the refcount in the host.
101   // This is because it only maintains one host refcount for all references
102   // in the plugin, but the host just sent the second one.
103   EXPECT_EQ(host_object.value.as_id, GetObjectIDForUniqueReleaseObject());
104   sink().ClearMessages();
105
106   // Release the object, one ref at a time. The second release should free
107   // the tracking data and send a release message to the browser.
108   var_tracker().ReleaseVar(plugin_object);
109   EXPECT_EQ(1, var_tracker().GetRefCountForObject(plugin_object));
110   var_tracker().ReleaseVar(plugin_object);
111   EXPECT_EQ(-1, var_tracker().GetRefCountForObject(plugin_object));
112   EXPECT_EQ(host_object.value.as_id, GetObjectIDForUniqueReleaseObject());
113 }
114
115 // Tests freeing objects that have both refcounts and "tracked with no ref".
116 TEST_F(PluginVarTrackerTest, FreeTrackedAndReferencedObject) {
117   ProxyAutoLock lock;
118   PP_Var host_object = MakeObject(12345);
119
120   // Phase one: First receive via a "pass ref", then a tracked with no ref.
121   PP_Var plugin_var = var_tracker().ReceiveObjectPassRef(
122       host_object, plugin_dispatcher());
123   PP_Var plugin_var2 = var_tracker().TrackObjectWithNoReference(
124       host_object, plugin_dispatcher());
125   EXPECT_EQ(plugin_var.value.as_id, plugin_var2.value.as_id);
126   EXPECT_EQ(1, var_tracker().GetRefCountForObject(plugin_var));
127   EXPECT_EQ(1,
128             var_tracker().GetTrackedWithNoReferenceCountForObject(plugin_var));
129
130   // Free via the refcount, this should release the object to the browser but
131   // maintain the tracked object.
132   var_tracker().ReleaseVar(plugin_var);
133   EXPECT_EQ(0, var_tracker().GetRefCountForObject(plugin_var));
134   EXPECT_EQ(1u, sink().message_count());
135   EXPECT_EQ(host_object.value.as_id, GetObjectIDForUniqueReleaseObject());
136
137   // Now free via the tracked object, this should free it.
138   var_tracker().StopTrackingObjectWithNoReference(plugin_var);
139   EXPECT_EQ(-1, var_tracker().GetRefCountForObject(plugin_var));
140
141   // Phase two: Receive via a tracked, then get an addref.
142   sink().ClearMessages();
143   plugin_var = var_tracker().TrackObjectWithNoReference(
144       host_object, plugin_dispatcher());
145   plugin_var2 = var_tracker().ReceiveObjectPassRef(
146       host_object, plugin_dispatcher());
147   EXPECT_EQ(plugin_var.value.as_id, plugin_var2.value.as_id);
148   EXPECT_EQ(1, var_tracker().GetRefCountForObject(plugin_var));
149   EXPECT_EQ(1,
150             var_tracker().GetTrackedWithNoReferenceCountForObject(plugin_var));
151
152   // Free via the tracked object, this should have no effect.
153   var_tracker().StopTrackingObjectWithNoReference(plugin_var);
154   EXPECT_EQ(0,
155             var_tracker().GetTrackedWithNoReferenceCountForObject(plugin_var));
156   EXPECT_EQ(0u, sink().message_count());
157
158   // Now free via the refcount, this should delete it.
159   var_tracker().ReleaseVar(plugin_var);
160   EXPECT_EQ(-1, var_tracker().GetRefCountForObject(plugin_var));
161   EXPECT_EQ(host_object.value.as_id, GetObjectIDForUniqueReleaseObject());
162 }
163
164 TEST_F(PluginVarTrackerTest, RecursiveTrackWithNoRef) {
165   ProxyAutoLock lock;
166   PP_Var host_object = MakeObject(12345);
167
168   // Receive a tracked object twice.
169   PP_Var plugin_var = var_tracker().TrackObjectWithNoReference(
170       host_object, plugin_dispatcher());
171   EXPECT_EQ(1,
172             var_tracker().GetTrackedWithNoReferenceCountForObject(plugin_var));
173   PP_Var plugin_var2 = var_tracker().TrackObjectWithNoReference(
174       host_object, plugin_dispatcher());
175   EXPECT_EQ(plugin_var.value.as_id, plugin_var2.value.as_id);
176   EXPECT_EQ(0, var_tracker().GetRefCountForObject(plugin_var));
177   EXPECT_EQ(2,
178             var_tracker().GetTrackedWithNoReferenceCountForObject(plugin_var));
179
180   // Now release those tracked items, the reference should be freed.
181   var_tracker().StopTrackingObjectWithNoReference(plugin_var);
182   EXPECT_EQ(1,
183             var_tracker().GetTrackedWithNoReferenceCountForObject(plugin_var));
184   var_tracker().StopTrackingObjectWithNoReference(plugin_var);
185   EXPECT_EQ(-1,
186             var_tracker().GetTrackedWithNoReferenceCountForObject(plugin_var));
187 }
188
189 // Tests that objects implemented by the plugin that have no references by
190 // the plugin get their Deallocate function called on destruction.
191 TEST_F(PluginVarTrackerTest, PluginObjectInstanceDeleted) {
192   ProxyAutoLock lock;
193   PP_Var host_object = MakeObject(12345);
194   PP_Instance pp_instance = 0x12345;
195
196   int deallocate_called = 0;
197   void* user_data = &deallocate_called;
198
199   // Make a var with one reference.
200   scoped_refptr<ProxyObjectVar> object(
201       new ProxyObjectVar(plugin_dispatcher(), host_object.value.as_id));
202   PP_Var plugin_var = MakeObject(var_tracker().AddVar(object.get()));
203   var_tracker().PluginImplementedObjectCreated(
204       pp_instance, plugin_var, &mark_on_deallocate_class, user_data);
205
206   // Release the plugin ref to the var. WebKit hasn't called destroy so
207   // we won't get a destroy call.
208   object = NULL;
209   var_tracker().ReleaseVar(plugin_var);
210   EXPECT_EQ(0, deallocate_called);
211
212   // Synthesize an instance destuction, this should call Deallocate.
213   var_tracker().DidDeleteInstance(pp_instance);
214   EXPECT_EQ(1, deallocate_called);
215 }
216
217 // Tests what happens when a plugin keeps a ref to a plugin-implemented
218 // object var longer than the instance. We should not call the destructor until
219 // the plugin releases its last ref.
220 TEST_F(PluginVarTrackerTest, PluginObjectLeaked) {
221   ProxyAutoLock lock;
222   PP_Var host_object = MakeObject(12345);
223   PP_Instance pp_instance = 0x12345;
224
225   int deallocate_called = 0;
226   void* user_data = &deallocate_called;
227
228   // Make a var with one reference.
229   scoped_refptr<ProxyObjectVar> object(
230       new ProxyObjectVar(plugin_dispatcher(), host_object.value.as_id));
231   PP_Var plugin_var = MakeObject(var_tracker().AddVar(object.get()));
232   var_tracker().PluginImplementedObjectCreated(
233       pp_instance, plugin_var, &mark_on_deallocate_class, user_data);
234
235   // Destroy the instance. This should not call deallocate since the plugin
236   // still has a ref.
237   var_tracker().DidDeleteInstance(pp_instance);
238   EXPECT_EQ(0, deallocate_called);
239
240   // Release the plugin ref to the var. Since the instance is gone this should
241   // call deallocate.
242   object = NULL;
243   var_tracker().ReleaseVar(plugin_var);
244   EXPECT_EQ(1, deallocate_called);
245 }
246
247 }  // namespace proxy
248 }  // namespace ppapi