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