Home | History | Annotate | Download | only in proxy
      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