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.
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"
17 PP_Var
MakeObject(int32 object_id
) {
19 ret
.type
= PP_VARTYPE_OBJECT
;
20 ret
.value
.as_id
= object_id
;
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.
26 void MarkOnDeallocate(void* object
) {
27 (*static_cast<int*>(object
))++;
30 // A class that just implements MarkOnDeallocate on destruction.
31 PPP_Class_Deprecated mark_on_deallocate_class
= {
35 NULL
, // GetAllPropertyNames,
37 NULL
, // RemoveProperty,
45 class PluginVarTrackerTest
: public PluginProxyTest
{
47 PluginVarTrackerTest() {}
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
);
59 PpapiHostMsg_PPBVar_ReleaseObject::Read(release_msg
, &id
);
64 TEST_F(PluginVarTrackerTest
, GetHostObject
) {
65 PP_Var host_object
= MakeObject(12345);
67 // Round-trip through the tracker to make sure the host object comes out the
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
);
75 var_tracker().ReleaseVar(plugin_object
);
78 TEST_F(PluginVarTrackerTest
, ReceiveObjectPassRef
) {
79 PP_Var host_object
= MakeObject(12345);
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
));
87 var_tracker().GetTrackedWithNoReferenceCountForObject(plugin_object
));
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
));
95 var_tracker().GetTrackedWithNoReferenceCountForObject(plugin_object
));
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();
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());
112 // Tests freeing objects that have both refcounts and "tracked with no ref".
113 TEST_F(PluginVarTrackerTest
, FreeTrackedAndReferencedObject
) {
114 PP_Var host_object
= MakeObject(12345);
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
));
124 var_tracker().GetTrackedWithNoReferenceCountForObject(plugin_var
));
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());
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
));
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
));
146 var_tracker().GetTrackedWithNoReferenceCountForObject(plugin_var
));
148 // Free via the tracked object, this should have no effect.
149 var_tracker().StopTrackingObjectWithNoReference(plugin_var
);
151 var_tracker().GetTrackedWithNoReferenceCountForObject(plugin_var
));
152 EXPECT_EQ(0u, sink().message_count());
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());
160 TEST_F(PluginVarTrackerTest
, RecursiveTrackWithNoRef
) {
161 PP_Var host_object
= MakeObject(12345);
163 // Receive a tracked object twice.
164 PP_Var plugin_var
= var_tracker().TrackObjectWithNoReference(
165 host_object
, plugin_dispatcher());
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
));
173 var_tracker().GetTrackedWithNoReferenceCountForObject(plugin_var
));
175 // Now release those tracked items, the reference should be freed.
176 var_tracker().StopTrackingObjectWithNoReference(plugin_var
);
178 var_tracker().GetTrackedWithNoReferenceCountForObject(plugin_var
));
179 var_tracker().StopTrackingObjectWithNoReference(plugin_var
);
181 var_tracker().GetTrackedWithNoReferenceCountForObject(plugin_var
));
184 // Tests that objects implemented by the plugin that have no references by
185 // the plugin get their Deallocate function called on destruction.
186 TEST_F(PluginVarTrackerTest
, PluginObjectInstanceDeleted
) {
187 PP_Var host_object
= MakeObject(12345);
188 PP_Instance pp_instance
= 0x12345;
190 int deallocate_called
= 0;
191 void* user_data
= &deallocate_called
;
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
,
199 &mark_on_deallocate_class
,
202 // Release the plugin ref to the var. WebKit hasn't called destroy so
203 // we won't get a destroy call.
205 var_tracker().ReleaseVar(plugin_var
);
206 EXPECT_EQ(0, deallocate_called
);
208 // Synthesize an instance destuction, this should call Deallocate.
209 var_tracker().DidDeleteInstance(pp_instance
);
210 EXPECT_EQ(1, deallocate_called
);
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.
216 TEST_F(PluginVarTrackerTest
, PluginObjectLeaked
) {
217 PP_Var host_object
= MakeObject(12345);
218 PP_Instance pp_instance
= 0x12345;
220 int deallocate_called
= 0;
221 void* user_data
= &deallocate_called
;
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
,
229 &mark_on_deallocate_class
,
232 // Destroy the instance. This should not call deallocate since the plugin
234 var_tracker().DidDeleteInstance(pp_instance
);
235 EXPECT_EQ(0, deallocate_called
);
237 // Release the plugin ref to the var. Since the instance is gone this should
240 var_tracker().ReleaseVar(plugin_var
);
241 EXPECT_EQ(1, deallocate_called
);