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