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 "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 #include "ppapi/shared_impl/proxy_lock.h"
     12 
     13 namespace ppapi {
     14 namespace proxy {
     15 
     16 namespace {
     17 
     18 PP_Var MakeObject(int32 object_id) {
     19   PP_Var ret;
     20   ret.type = PP_VARTYPE_OBJECT;
     21   ret.value.as_id = object_id;
     22   return ret;
     23 }
     24 
     25 // A Deallocate() function for PPP_Class that just increments the integer
     26 // referenced by the pointer so we know how often Deallocate was called.
     27 void MarkOnDeallocate(void* object) {
     28   (*static_cast<int*>(object))++;
     29 }
     30 
     31 // A class that just implements MarkOnDeallocate on destruction.
     32 PPP_Class_Deprecated mark_on_deallocate_class = {
     33   NULL,  // HasProperty,
     34   NULL,  // HasMethod,
     35   NULL,  // GetProperty,
     36   NULL,  // GetAllPropertyNames,
     37   NULL,  // SetProperty,
     38   NULL,  // RemoveProperty,
     39   NULL,  // Call,
     40   NULL,  // Construct,
     41   &MarkOnDeallocate
     42 };
     43 
     44 }  // namespace
     45 
     46 class PluginVarTrackerTest : public PluginProxyTest {
     47  public:
     48   PluginVarTrackerTest() {}
     49 
     50  protected:
     51   // Asserts that there is a unique "release object" IPC message in the test
     52   // sink. This will return the var ID from the message or -1 if none found.
     53   int32 GetObjectIDForUniqueReleaseObject() {
     54     const IPC::Message* release_msg = sink().GetUniqueMessageMatching(
     55         PpapiHostMsg_PPBVar_ReleaseObject::ID);
     56     if (!release_msg)
     57       return -1;
     58 
     59     Tuple1<int64> id;
     60     PpapiHostMsg_PPBVar_ReleaseObject::Read(release_msg, &id);
     61     return id.a;
     62   }
     63 };
     64 
     65 TEST_F(PluginVarTrackerTest, GetHostObject) {
     66   ProxyAutoLock lock;
     67   PP_Var host_object = MakeObject(12345);
     68 
     69   // Round-trip through the tracker to make sure the host object comes out the
     70   // other end.
     71   PP_Var plugin_object = var_tracker().ReceiveObjectPassRef(
     72       host_object, plugin_dispatcher());
     73   PP_Var host_object2 = var_tracker().GetHostObject(plugin_object);
     74   EXPECT_EQ(PP_VARTYPE_OBJECT, host_object2.type);
     75   EXPECT_EQ(host_object.value.as_id, host_object2.value.as_id);
     76 
     77   var_tracker().ReleaseVar(plugin_object);
     78 }
     79 
     80 TEST_F(PluginVarTrackerTest, ReceiveObjectPassRef) {
     81   ProxyAutoLock lock;
     82   PP_Var host_object = MakeObject(12345);
     83 
     84   // Receive the object, we should have one ref and no messages.
     85   PP_Var plugin_object = var_tracker().ReceiveObjectPassRef(
     86       host_object, plugin_dispatcher());
     87   EXPECT_EQ(0u, sink().message_count());
     88   EXPECT_EQ(1, var_tracker().GetRefCountForObject(plugin_object));
     89   EXPECT_EQ(0,
     90       var_tracker().GetTrackedWithNoReferenceCountForObject(plugin_object));
     91 
     92   // Receive the same object again, we should get the same plugin ID out.
     93   PP_Var plugin_object2 = var_tracker().ReceiveObjectPassRef(
     94       host_object, plugin_dispatcher());
     95   EXPECT_EQ(plugin_object.value.as_id, plugin_object2.value.as_id);
     96   EXPECT_EQ(2, var_tracker().GetRefCountForObject(plugin_object));
     97   EXPECT_EQ(0,
     98       var_tracker().GetTrackedWithNoReferenceCountForObject(plugin_object));
     99 
    100   // It should have sent one message to decerment the refcount in the host.
    101   // This is because it only maintains one host refcount for all references
    102   // in the plugin, but the host just sent the second one.
    103   EXPECT_EQ(host_object.value.as_id, GetObjectIDForUniqueReleaseObject());
    104   sink().ClearMessages();
    105 
    106   // Release the object, one ref at a time. The second release should free
    107   // the tracking data and send a release message to the browser.
    108   var_tracker().ReleaseVar(plugin_object);
    109   EXPECT_EQ(1, var_tracker().GetRefCountForObject(plugin_object));
    110   var_tracker().ReleaseVar(plugin_object);
    111   EXPECT_EQ(-1, var_tracker().GetRefCountForObject(plugin_object));
    112   EXPECT_EQ(host_object.value.as_id, GetObjectIDForUniqueReleaseObject());
    113 }
    114 
    115 // Tests freeing objects that have both refcounts and "tracked with no ref".
    116 TEST_F(PluginVarTrackerTest, FreeTrackedAndReferencedObject) {
    117   ProxyAutoLock lock;
    118   PP_Var host_object = MakeObject(12345);
    119 
    120   // Phase one: First receive via a "pass ref", then a tracked with no ref.
    121   PP_Var plugin_var = var_tracker().ReceiveObjectPassRef(
    122       host_object, plugin_dispatcher());
    123   PP_Var plugin_var2 = var_tracker().TrackObjectWithNoReference(
    124       host_object, plugin_dispatcher());
    125   EXPECT_EQ(plugin_var.value.as_id, plugin_var2.value.as_id);
    126   EXPECT_EQ(1, var_tracker().GetRefCountForObject(plugin_var));
    127   EXPECT_EQ(1,
    128             var_tracker().GetTrackedWithNoReferenceCountForObject(plugin_var));
    129 
    130   // Free via the refcount, this should release the object to the browser but
    131   // maintain the tracked object.
    132   var_tracker().ReleaseVar(plugin_var);
    133   EXPECT_EQ(0, var_tracker().GetRefCountForObject(plugin_var));
    134   EXPECT_EQ(1u, sink().message_count());
    135   EXPECT_EQ(host_object.value.as_id, GetObjectIDForUniqueReleaseObject());
    136 
    137   // Now free via the tracked object, this should free it.
    138   var_tracker().StopTrackingObjectWithNoReference(plugin_var);
    139   EXPECT_EQ(-1, var_tracker().GetRefCountForObject(plugin_var));
    140 
    141   // Phase two: Receive via a tracked, then get an addref.
    142   sink().ClearMessages();
    143   plugin_var = var_tracker().TrackObjectWithNoReference(
    144       host_object, plugin_dispatcher());
    145   plugin_var2 = var_tracker().ReceiveObjectPassRef(
    146       host_object, plugin_dispatcher());
    147   EXPECT_EQ(plugin_var.value.as_id, plugin_var2.value.as_id);
    148   EXPECT_EQ(1, var_tracker().GetRefCountForObject(plugin_var));
    149   EXPECT_EQ(1,
    150             var_tracker().GetTrackedWithNoReferenceCountForObject(plugin_var));
    151 
    152   // Free via the tracked object, this should have no effect.
    153   var_tracker().StopTrackingObjectWithNoReference(plugin_var);
    154   EXPECT_EQ(0,
    155             var_tracker().GetTrackedWithNoReferenceCountForObject(plugin_var));
    156   EXPECT_EQ(0u, sink().message_count());
    157 
    158   // Now free via the refcount, this should delete it.
    159   var_tracker().ReleaseVar(plugin_var);
    160   EXPECT_EQ(-1, var_tracker().GetRefCountForObject(plugin_var));
    161   EXPECT_EQ(host_object.value.as_id, GetObjectIDForUniqueReleaseObject());
    162 }
    163 
    164 TEST_F(PluginVarTrackerTest, RecursiveTrackWithNoRef) {
    165   ProxyAutoLock lock;
    166   PP_Var host_object = MakeObject(12345);
    167 
    168   // Receive a tracked object twice.
    169   PP_Var plugin_var = var_tracker().TrackObjectWithNoReference(
    170       host_object, plugin_dispatcher());
    171   EXPECT_EQ(1,
    172             var_tracker().GetTrackedWithNoReferenceCountForObject(plugin_var));
    173   PP_Var plugin_var2 = var_tracker().TrackObjectWithNoReference(
    174       host_object, plugin_dispatcher());
    175   EXPECT_EQ(plugin_var.value.as_id, plugin_var2.value.as_id);
    176   EXPECT_EQ(0, var_tracker().GetRefCountForObject(plugin_var));
    177   EXPECT_EQ(2,
    178             var_tracker().GetTrackedWithNoReferenceCountForObject(plugin_var));
    179 
    180   // Now release those tracked items, the reference should be freed.
    181   var_tracker().StopTrackingObjectWithNoReference(plugin_var);
    182   EXPECT_EQ(1,
    183             var_tracker().GetTrackedWithNoReferenceCountForObject(plugin_var));
    184   var_tracker().StopTrackingObjectWithNoReference(plugin_var);
    185   EXPECT_EQ(-1,
    186             var_tracker().GetTrackedWithNoReferenceCountForObject(plugin_var));
    187 }
    188 
    189 // Tests that objects implemented by the plugin that have no references by
    190 // the plugin get their Deallocate function called on destruction.
    191 TEST_F(PluginVarTrackerTest, PluginObjectInstanceDeleted) {
    192   ProxyAutoLock lock;
    193   PP_Var host_object = MakeObject(12345);
    194   PP_Instance pp_instance = 0x12345;
    195 
    196   int deallocate_called = 0;
    197   void* user_data = &deallocate_called;
    198 
    199   // Make a var with one reference.
    200   scoped_refptr<ProxyObjectVar> object(
    201       new ProxyObjectVar(plugin_dispatcher(), host_object.value.as_id));
    202   PP_Var plugin_var = MakeObject(var_tracker().AddVar(object.get()));
    203   var_tracker().PluginImplementedObjectCreated(
    204       pp_instance, plugin_var, &mark_on_deallocate_class, user_data);
    205 
    206   // Release the plugin ref to the var. WebKit hasn't called destroy so
    207   // we won't get a destroy call.
    208   object = NULL;
    209   var_tracker().ReleaseVar(plugin_var);
    210   EXPECT_EQ(0, deallocate_called);
    211 
    212   // Synthesize an instance destuction, this should call Deallocate.
    213   var_tracker().DidDeleteInstance(pp_instance);
    214   EXPECT_EQ(1, deallocate_called);
    215 }
    216 
    217 // Tests what happens when a plugin keeps a ref to a plugin-implemented
    218 // object var longer than the instance. We should not call the destructor until
    219 // the plugin releases its last ref.
    220 TEST_F(PluginVarTrackerTest, PluginObjectLeaked) {
    221   ProxyAutoLock lock;
    222   PP_Var host_object = MakeObject(12345);
    223   PP_Instance pp_instance = 0x12345;
    224 
    225   int deallocate_called = 0;
    226   void* user_data = &deallocate_called;
    227 
    228   // Make a var with one reference.
    229   scoped_refptr<ProxyObjectVar> object(
    230       new ProxyObjectVar(plugin_dispatcher(), host_object.value.as_id));
    231   PP_Var plugin_var = MakeObject(var_tracker().AddVar(object.get()));
    232   var_tracker().PluginImplementedObjectCreated(
    233       pp_instance, plugin_var, &mark_on_deallocate_class, user_data);
    234 
    235   // Destroy the instance. This should not call deallocate since the plugin
    236   // still has a ref.
    237   var_tracker().DidDeleteInstance(pp_instance);
    238   EXPECT_EQ(0, deallocate_called);
    239 
    240   // Release the plugin ref to the var. Since the instance is gone this should
    241   // call deallocate.
    242   object = NULL;
    243   var_tracker().ReleaseVar(plugin_var);
    244   EXPECT_EQ(1, deallocate_called);
    245 }
    246 
    247 }  // namespace proxy
    248 }  // namespace ppapi
    249