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 "ppapi/proxy/ppapi_proxy_test.h" 6 7 #include "ppapi/proxy/serialized_var.h" 8 #include "ppapi/shared_impl/proxy_lock.h" 9 10 namespace ppapi { 11 namespace proxy { 12 13 namespace { 14 15 PP_Var MakeObjectVar(int64_t object_id) { 16 PP_Var ret; 17 ret.type = PP_VARTYPE_OBJECT; 18 ret.value.as_id = object_id; 19 return ret; 20 } 21 22 class SerializedVarTest : public PluginProxyTest { 23 public: 24 SerializedVarTest() {} 25 }; 26 27 } // namespace 28 29 // Tests output arguments in the plugin. This is when the host calls into the 30 // plugin and the plugin returns something via an out param, like an exception. 31 TEST_F(SerializedVarTest, PluginSerializedVarInOutParam) { 32 ProxyAutoLock lock; 33 PP_Var host_object = MakeObjectVar(0x31337); 34 35 PP_Var plugin_object; 36 { 37 // Receive the object param, we should be tracking it with no refcount, and 38 // no messages sent. 39 SerializedVarTestConstructor input(host_object); 40 SerializedVarReceiveInput receive_input(input); 41 plugin_object = receive_input.Get(plugin_dispatcher()); 42 EXPECT_EQ(0, var_tracker().GetRefCountForObject(plugin_object)); 43 EXPECT_EQ(0u, sink().message_count()); 44 45 SerializedVar sv; 46 { 47 // The "OutParam" does its work in its destructor, it will write the 48 // information to the SerializedVar we passed in the constructor. 49 SerializedVarOutParam out_param(&sv); 50 // An out-param needs to pass a reference to the caller, so it's the 51 // responsibility of the plugin to bump the ref-count on an input 52 // parameter. 53 var_tracker().AddRefVar(plugin_object); 54 EXPECT_EQ(1, var_tracker().GetRefCountForObject(plugin_object)); 55 // We should have informed the host that a reference was taken. 56 EXPECT_EQ(1u, sink().message_count()); 57 *out_param.OutParam(plugin_dispatcher()) = plugin_object; 58 } 59 60 // The object should have transformed the plugin object back to the host 61 // object ID. Nothing in the var tracker should have changed yet, and no 62 // messages should have been sent. 63 SerializedVarTestReader reader(sv); 64 EXPECT_EQ(host_object.value.as_id, reader.GetVar().value.as_id); 65 EXPECT_EQ(1, var_tracker().GetRefCountForObject(plugin_object)); 66 EXPECT_EQ(1u, sink().message_count()); 67 } 68 69 // The out param should have done an "end receive caller owned" on the plugin 70 // var serialization rules, which should have released the "track-with-no- 71 // reference" count in the var tracker as well as the 1 reference we passed 72 // back to the host, so the object should no longer be in the tracker. The 73 // reference we added has been removed, so another message should be sent to 74 // the host to tell it we're done with the object. 75 EXPECT_EQ(-1, var_tracker().GetRefCountForObject(plugin_object)); 76 EXPECT_EQ(2u, sink().message_count()); 77 } 78 79 // Tests output strings in the plugin. This is when the host calls into the 80 // plugin with a string and the plugin returns it via an out param. 81 TEST_F(SerializedVarTest, PluginSerializedStringVarInOutParam) { 82 ProxyAutoLock lock; 83 PP_Var plugin_string; 84 const std::string kTestString("elite"); 85 { 86 // Receive the string param. We should track it with 1 refcount. 87 SerializedVarTestConstructor input(kTestString); 88 SerializedVarReceiveInput receive_input(input); 89 plugin_string = receive_input.Get(plugin_dispatcher()); 90 EXPECT_EQ(1, var_tracker().GetRefCountForObject(plugin_string)); 91 EXPECT_EQ(0u, sink().message_count()); 92 93 SerializedVar sv; 94 { 95 // The "OutParam" does its work in its destructor, it will write the 96 // information to the SerializedVar we passed in the constructor. 97 SerializedVarOutParam out_param(&sv); 98 // An out-param needs to pass a reference to the caller, so it's the 99 // responsibility of the plugin to bump the ref-count of an input 100 // parameter. 101 var_tracker().AddRefVar(plugin_string); 102 EXPECT_EQ(2, var_tracker().GetRefCountForObject(plugin_string)); 103 EXPECT_EQ(0u, sink().message_count()); 104 *out_param.OutParam(plugin_dispatcher()) = plugin_string; 105 } 106 107 // The SerializedVar should have set the string value internally. Nothing in 108 // the var tracker should have changed yet, and no messages should have been 109 // sent. 110 SerializedVarTestReader reader(sv); 111 //EXPECT_EQ(kTestString, *reader.GetTrackerStringPtr()); 112 EXPECT_EQ(2, var_tracker().GetRefCountForObject(plugin_string)); 113 EXPECT_EQ(0u, sink().message_count()); 114 } 115 // The reference the string had initially should be gone, and the reference we 116 // passed to the host should also be gone, so the string should be removed. 117 EXPECT_EQ(-1, var_tracker().GetRefCountForObject(plugin_string)); 118 EXPECT_EQ(0u, sink().message_count()); 119 } 120 121 // Tests receiving an argument and passing it back to the browser as an output 122 // parameter. 123 TEST_F(SerializedVarTest, PluginSerializedVarOutParam) { 124 ProxyAutoLock lock; 125 PP_Var host_object = MakeObjectVar(0x31337); 126 127 // Start tracking this object in the plugin. 128 PP_Var plugin_object = var_tracker().ReceiveObjectPassRef( 129 host_object, plugin_dispatcher()); 130 EXPECT_EQ(1, var_tracker().GetRefCountForObject(plugin_object)); 131 132 { 133 SerializedVar sv; 134 { 135 // The "OutParam" does its work in its destructor, it will write the 136 // information to the SerializedVar we passed in the constructor. 137 SerializedVarOutParam out_param(&sv); 138 *out_param.OutParam(plugin_dispatcher()) = plugin_object; 139 } 140 141 // The object should have transformed the plugin object back to the host 142 // object ID. Nothing in the var tracker should have changed yet, and no 143 // messages should have been sent. 144 SerializedVarTestReader reader(sv); 145 EXPECT_EQ(host_object.value.as_id, reader.GetVar().value.as_id); 146 EXPECT_EQ(1, var_tracker().GetRefCountForObject(plugin_object)); 147 EXPECT_EQ(0u, sink().message_count()); 148 } 149 150 // The out param should have done an "end send pass ref" on the plugin 151 // var serialization rules, which should have in turn released the reference 152 // in the var tracker. Since we only had one reference, this should have sent 153 // a release to the browser. 154 EXPECT_EQ(-1, var_tracker().GetRefCountForObject(plugin_object)); 155 EXPECT_EQ(1u, sink().message_count()); 156 157 // We don't bother validating that message since it's nontrivial and the 158 // PluginVarTracker test has cases that cover that this message is correct. 159 } 160 161 // Tests the case that the plugin receives the same var twice as an input 162 // parameter (not passing ownership). 163 TEST_F(SerializedVarTest, PluginReceiveInput) { 164 ProxyAutoLock lock; 165 PP_Var host_object = MakeObjectVar(0x31337); 166 167 PP_Var plugin_object; 168 { 169 // Receive the first param, we should be tracking it with no refcount, and 170 // no messages sent. 171 SerializedVarTestConstructor input1(host_object); 172 SerializedVarReceiveInput receive_input(input1); 173 plugin_object = receive_input.Get(plugin_dispatcher()); 174 EXPECT_EQ(0, var_tracker().GetRefCountForObject(plugin_object)); 175 EXPECT_EQ(0u, sink().message_count()); 176 177 // Receive the second param, it should be resolved to the same plugin 178 // object and there should still be no refcount. 179 SerializedVarTestConstructor input2(host_object); 180 SerializedVarReceiveInput receive_input2(input2); 181 PP_Var plugin_object2 = receive_input2.Get(plugin_dispatcher()); 182 EXPECT_EQ(plugin_object.value.as_id, plugin_object2.value.as_id); 183 EXPECT_EQ(0, var_tracker().GetRefCountForObject(plugin_object)); 184 EXPECT_EQ(0u, sink().message_count()); 185 186 // Take a reference to the object, as if the plugin was using it, and then 187 // release it, we should still be tracking the object since the 188 // ReceiveInputs keep the "track_with_no_reference_count" alive until 189 // they're destroyed. 190 var_tracker().AddRefVar(plugin_object); 191 EXPECT_EQ(1, var_tracker().GetRefCountForObject(plugin_object)); 192 var_tracker().ReleaseVar(plugin_object); 193 EXPECT_EQ(0, var_tracker().GetRefCountForObject(plugin_object)); 194 EXPECT_EQ(2u, sink().message_count()); 195 } 196 197 // Since we didn't keep any refs to the objects, it should have freed the 198 // object. 199 EXPECT_EQ(-1, var_tracker().GetRefCountForObject(plugin_object)); 200 } 201 202 // Tests the case that the plugin receives the same vars twice as an input 203 // parameter (not passing ownership) within a vector. 204 TEST_F(SerializedVarTest, PluginVectorReceiveInput) { 205 ProxyAutoLock lock; 206 PP_Var host_object = MakeObjectVar(0x31337); 207 208 std::vector<PP_Var> plugin_objects; 209 std::vector<PP_Var> plugin_objects2; 210 { 211 // Receive the params. The object should be tracked with no refcount and 212 // no messages sent. The string should is plugin-side only and should have 213 // a reference-count of 1. 214 std::vector<SerializedVar> input1; 215 input1.push_back(SerializedVarTestConstructor(host_object)); 216 input1.push_back(SerializedVarTestConstructor("elite")); 217 SerializedVarVectorReceiveInput receive_input(input1); 218 uint32_t array_size = 0; 219 PP_Var* plugin_objects_array = 220 receive_input.Get(plugin_dispatcher(), &array_size); 221 plugin_objects.insert(plugin_objects.begin(), plugin_objects_array, 222 plugin_objects_array + array_size); 223 ASSERT_EQ(2u, array_size); 224 EXPECT_EQ(0, var_tracker().GetRefCountForObject(plugin_objects[0])); 225 EXPECT_EQ(1, var_tracker().GetRefCountForObject(plugin_objects[1])); 226 EXPECT_EQ(0u, sink().message_count()); 227 228 // Receive the second param, it should be resolved to the same plugin 229 // object and there should still be no refcount. 230 std::vector<SerializedVar> input2; 231 input2.push_back(SerializedVarTestConstructor(host_object)); 232 input2.push_back(SerializedVarTestConstructor("elite")); 233 SerializedVarVectorReceiveInput receive_input2(input2); 234 uint32_t array_size2 = 0; 235 PP_Var* plugin_objects_array2 = 236 receive_input2.Get(plugin_dispatcher(), &array_size2); 237 plugin_objects2.insert(plugin_objects2.begin(), plugin_objects_array2, 238 plugin_objects_array2 + array_size2); 239 ASSERT_EQ(2u, array_size2); 240 EXPECT_EQ(plugin_objects[0].value.as_id, plugin_objects2[0].value.as_id); 241 EXPECT_EQ(0, var_tracker().GetRefCountForObject(plugin_objects[0])); 242 // Strings get re-created with a new ID. We don't try to reuse strings in 243 // the tracker, so the string should get a new ID. 244 EXPECT_NE(plugin_objects[1].value.as_id, plugin_objects2[1].value.as_id); 245 EXPECT_EQ(1, var_tracker().GetRefCountForObject(plugin_objects2[1])); 246 EXPECT_EQ(0u, sink().message_count()); 247 248 // Take a reference to the object, as if the plugin was using it, and then 249 // release it, we should still be tracking the object since the 250 // ReceiveInputs keep the "track_with_no_reference_count" alive until 251 // they're destroyed. 252 var_tracker().AddRefVar(plugin_objects[0]); 253 EXPECT_EQ(1, var_tracker().GetRefCountForObject(plugin_objects[0])); 254 var_tracker().ReleaseVar(plugin_objects[0]); 255 EXPECT_EQ(0, var_tracker().GetRefCountForObject(plugin_objects[0])); 256 EXPECT_EQ(2u, sink().message_count()); 257 258 // Take a reference to a string and then release it. Make sure no messages 259 // are sent. 260 uint32_t old_message_count = sink().message_count(); 261 var_tracker().AddRefVar(plugin_objects[1]); 262 EXPECT_EQ(2, var_tracker().GetRefCountForObject(plugin_objects[1])); 263 var_tracker().ReleaseVar(plugin_objects[1]); 264 EXPECT_EQ(1, var_tracker().GetRefCountForObject(plugin_objects[1])); 265 EXPECT_EQ(old_message_count, sink().message_count()); 266 } 267 268 // Since we didn't keep any refs to the objects or strings, so they should 269 // have been freed. 270 EXPECT_EQ(-1, var_tracker().GetRefCountForObject(plugin_objects[0])); 271 EXPECT_EQ(-1, var_tracker().GetRefCountForObject(plugin_objects[1])); 272 EXPECT_EQ(-1, var_tracker().GetRefCountForObject(plugin_objects2[1])); 273 } 274 275 // Tests the plugin receiving a var as a return value from the browser 276 // two different times (passing ownership). 277 TEST_F(SerializedVarTest, PluginReceiveReturn) { 278 ProxyAutoLock lock; 279 PP_Var host_object = MakeObjectVar(0x31337); 280 281 PP_Var plugin_object; 282 { 283 // Receive the first param, we should be tracking it with a refcount of 1. 284 SerializedVarTestConstructor input1(host_object); 285 ReceiveSerializedVarReturnValue receive_input(input1); 286 plugin_object = receive_input.Return(plugin_dispatcher()); 287 EXPECT_EQ(1, var_tracker().GetRefCountForObject(plugin_object)); 288 EXPECT_EQ(0u, sink().message_count()); 289 290 // Receive the second param, it should be resolved to the same plugin 291 // object and there should be a plugin refcount of 2. There should have 292 // been an IPC message sent that released the duplicated ref in the browser 293 // (so both of our refs are represented by one in the browser). 294 SerializedVarTestConstructor input2(host_object); 295 ReceiveSerializedVarReturnValue receive_input2(input2); 296 PP_Var plugin_object2 = receive_input2.Return(plugin_dispatcher()); 297 EXPECT_EQ(plugin_object.value.as_id, plugin_object2.value.as_id); 298 EXPECT_EQ(2, var_tracker().GetRefCountForObject(plugin_object)); 299 EXPECT_EQ(1u, sink().message_count()); 300 } 301 302 // The ReceiveSerializedVarReturnValue destructor shouldn't have affected 303 // the refcount or sent any messages. 304 EXPECT_EQ(2, var_tracker().GetRefCountForObject(plugin_object)); 305 EXPECT_EQ(1u, sink().message_count()); 306 307 // Manually release one refcount, it shouldn't have sent any more messages. 308 var_tracker().ReleaseVar(plugin_object); 309 EXPECT_EQ(1, var_tracker().GetRefCountForObject(plugin_object)); 310 EXPECT_EQ(1u, sink().message_count()); 311 312 // Manually release the last refcount, it should have freed it and sent a 313 // release message to the browser. 314 var_tracker().ReleaseVar(plugin_object); 315 EXPECT_EQ(-1, var_tracker().GetRefCountForObject(plugin_object)); 316 EXPECT_EQ(2u, sink().message_count()); 317 } 318 319 // Returns a value from the browser to the plugin, then return that one ref 320 // back to the browser. 321 TEST_F(SerializedVarTest, PluginReturnValue) { 322 ProxyAutoLock lock; 323 PP_Var host_object = MakeObjectVar(0x31337); 324 325 PP_Var plugin_object; 326 { 327 // Receive the param in the plugin. 328 SerializedVarTestConstructor input1(host_object); 329 ReceiveSerializedVarReturnValue receive_input(input1); 330 plugin_object = receive_input.Return(plugin_dispatcher()); 331 EXPECT_EQ(1, var_tracker().GetRefCountForObject(plugin_object)); 332 EXPECT_EQ(0u, sink().message_count()); 333 } 334 335 { 336 // Now return to the browser. 337 SerializedVar output; 338 SerializedVarReturnValue return_output(&output); 339 return_output.Return(plugin_dispatcher(), plugin_object); 340 341 // The ref in the plugin should be alive until the ReturnValue goes out of 342 // scope, since the release needs to be after the browser processes the 343 // message. 344 EXPECT_EQ(1, var_tracker().GetRefCountForObject(plugin_object)); 345 } 346 347 // When the ReturnValue object goes out of scope, it should have sent a 348 // release message to the browser. 349 EXPECT_EQ(-1, var_tracker().GetRefCountForObject(plugin_object)); 350 EXPECT_EQ(1u, sink().message_count()); 351 } 352 353 } // namespace proxy 354 } // namespace ppapi 355