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
10namespace ppapi {
11namespace proxy {
12
13namespace {
14
15PP_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
22class 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.
31TEST_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.
81TEST_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.
123TEST_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).
163TEST_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.
204TEST_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).
277TEST_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.
321TEST_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