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/plugin_var_tracker.h"
      6 
      7 #include "base/memory/ref_counted.h"
      8 #include "base/memory/singleton.h"
      9 #include "ppapi/c/dev/ppp_class_deprecated.h"
     10 #include "ppapi/c/ppb_var.h"
     11 #include "ppapi/proxy/plugin_array_buffer_var.h"
     12 #include "ppapi/proxy/plugin_dispatcher.h"
     13 #include "ppapi/proxy/ppapi_messages.h"
     14 #include "ppapi/proxy/proxy_object_var.h"
     15 #include "ppapi/shared_impl/api_id.h"
     16 #include "ppapi/shared_impl/proxy_lock.h"
     17 #include "ppapi/shared_impl/var.h"
     18 
     19 namespace ppapi {
     20 namespace proxy {
     21 
     22 PluginVarTracker::HostVar::HostVar(PluginDispatcher* d, int32 i)
     23     : dispatcher(d),
     24       host_object_id(i) {
     25 }
     26 
     27 bool PluginVarTracker::HostVar::operator<(const HostVar& other) const {
     28   if (dispatcher < other.dispatcher)
     29     return true;
     30   if (other.dispatcher < dispatcher)
     31     return false;
     32   return host_object_id < other.host_object_id;
     33 }
     34 
     35 PluginVarTracker::PluginVarTracker() : VarTracker(THREAD_SAFE) {
     36 }
     37 
     38 PluginVarTracker::~PluginVarTracker() {
     39 }
     40 
     41 PP_Var PluginVarTracker::ReceiveObjectPassRef(const PP_Var& host_var,
     42                                               PluginDispatcher* dispatcher) {
     43   CheckThreadingPreconditions();
     44   DCHECK(host_var.type == PP_VARTYPE_OBJECT);
     45 
     46   // Get the object.
     47   scoped_refptr<ProxyObjectVar> object(
     48       FindOrMakePluginVarFromHostVar(host_var, dispatcher));
     49 
     50   // Actually create the PP_Var, this will add all the tracking info but not
     51   // adjust any refcounts.
     52   PP_Var ret = GetOrCreateObjectVarID(object.get());
     53 
     54   VarInfo& info = GetLiveVar(ret)->second;
     55   if (info.ref_count > 0) {
     56     // We already had a reference to it before. That means the renderer now has
     57     // two references on our behalf. We want to transfer that extra reference
     58     // to our list. This means we addref in the plugin, and release the extra
     59     // one in the renderer.
     60     SendReleaseObjectMsg(*object.get());
     61   }
     62   info.ref_count++;
     63   return ret;
     64 }
     65 
     66 PP_Var PluginVarTracker::TrackObjectWithNoReference(
     67     const PP_Var& host_var,
     68     PluginDispatcher* dispatcher) {
     69   CheckThreadingPreconditions();
     70   DCHECK(host_var.type == PP_VARTYPE_OBJECT);
     71 
     72   // Get the object.
     73   scoped_refptr<ProxyObjectVar> object(
     74       FindOrMakePluginVarFromHostVar(host_var, dispatcher));
     75 
     76   // Actually create the PP_Var, this will add all the tracking info but not
     77   // adjust any refcounts.
     78   PP_Var ret = GetOrCreateObjectVarID(object.get());
     79 
     80   VarInfo& info = GetLiveVar(ret)->second;
     81   info.track_with_no_reference_count++;
     82   return ret;
     83 }
     84 
     85 void PluginVarTracker::StopTrackingObjectWithNoReference(
     86     const PP_Var& plugin_var) {
     87   CheckThreadingPreconditions();
     88   DCHECK(plugin_var.type == PP_VARTYPE_OBJECT);
     89 
     90   VarMap::iterator found = GetLiveVar(plugin_var);
     91   if (found == live_vars_.end()) {
     92     NOTREACHED();
     93     return;
     94   }
     95 
     96   DCHECK(found->second.track_with_no_reference_count > 0);
     97   found->second.track_with_no_reference_count--;
     98   DeleteObjectInfoIfNecessary(found);
     99 }
    100 
    101 PP_Var PluginVarTracker::GetHostObject(const PP_Var& plugin_object) const {
    102   CheckThreadingPreconditions();
    103   if (plugin_object.type != PP_VARTYPE_OBJECT) {
    104     NOTREACHED();
    105     return PP_MakeUndefined();
    106   }
    107 
    108   Var* var = GetVar(plugin_object);
    109   ProxyObjectVar* object = var->AsProxyObjectVar();
    110   if (!object) {
    111     NOTREACHED();
    112     return PP_MakeUndefined();
    113   }
    114 
    115   // Make a var with the host ID.
    116   PP_Var ret = { PP_VARTYPE_OBJECT };
    117   ret.value.as_id = object->host_var_id();
    118   return ret;
    119 }
    120 
    121 PluginDispatcher* PluginVarTracker::DispatcherForPluginObject(
    122     const PP_Var& plugin_object) const {
    123   CheckThreadingPreconditions();
    124   if (plugin_object.type != PP_VARTYPE_OBJECT)
    125     return NULL;
    126 
    127   VarMap::const_iterator found = GetLiveVar(plugin_object);
    128   if (found == live_vars_.end())
    129     return NULL;
    130 
    131   ProxyObjectVar* object = found->second.var->AsProxyObjectVar();
    132   if (!object)
    133     return NULL;
    134   return object->dispatcher();
    135 }
    136 
    137 void PluginVarTracker::ReleaseHostObject(PluginDispatcher* dispatcher,
    138                                          const PP_Var& host_object) {
    139   CheckThreadingPreconditions();
    140   DCHECK(host_object.type == PP_VARTYPE_OBJECT);
    141 
    142   // Convert the host object to a normal var valid in the plugin.
    143   HostVarToPluginVarMap::iterator found = host_var_to_plugin_var_.find(
    144       HostVar(dispatcher, static_cast<int32>(host_object.value.as_id)));
    145   if (found == host_var_to_plugin_var_.end()) {
    146     NOTREACHED();
    147     return;
    148   }
    149 
    150   // Now just release the object given the plugin var ID.
    151   ReleaseVar(found->second);
    152 }
    153 
    154 void PluginVarTracker::DidDeleteInstance(PP_Instance instance) {
    155   // Calling the destructors on plugin objects may in turn release other
    156   // objects which will mutate the map out from under us. So do a two-step
    157   // process of identifying the ones to delete, and then delete them.
    158   //
    159   // See the comment above user_data_to_plugin_ in the header file. We assume
    160   // there aren't that many objects so a brute-force search is reasonable.
    161   std::vector<void*> user_data_to_delete;
    162   for (UserDataToPluginImplementedVarMap::const_iterator i =
    163            user_data_to_plugin_.begin();
    164        i != user_data_to_plugin_.end();
    165        ++i) {
    166     if (i->second.instance == instance)
    167       user_data_to_delete.push_back(i->first);
    168   }
    169 
    170   for (size_t i = 0; i < user_data_to_delete.size(); i++) {
    171     UserDataToPluginImplementedVarMap::iterator found =
    172         user_data_to_plugin_.find(user_data_to_delete[i]);
    173     if (found == user_data_to_plugin_.end())
    174       continue;  // Object removed from list while we were iterating.
    175 
    176     if (!found->second.plugin_object_id) {
    177       // This object is for the freed instance and the plugin is not holding
    178       // any references to it. Deallocate immediately.
    179       CallWhileUnlocked(found->second.ppp_class->Deallocate, found->first);
    180       user_data_to_plugin_.erase(found);
    181     } else {
    182       // The plugin is holding refs to this object. We don't want to call
    183       // Deallocate since the plugin may be depending on those refs to keep
    184       // its data alive. To avoid crashes in this case, just clear out the
    185       // instance to mark it and continue. When the plugin refs go to 0,
    186       // we'll notice there is no instance and call Deallocate.
    187       found->second.instance = 0;
    188     }
    189   }
    190 }
    191 
    192 void PluginVarTracker::DidDeleteDispatcher(PluginDispatcher* dispatcher) {
    193   for (VarMap::iterator it = live_vars_.begin();
    194        it != live_vars_.end();
    195        ++it) {
    196     if (it->second.var.get() == NULL)
    197       continue;
    198     ProxyObjectVar* object = it->second.var->AsProxyObjectVar();
    199     if (object && object->dispatcher() == dispatcher)
    200       object->clear_dispatcher();
    201   }
    202 }
    203 
    204 ArrayBufferVar* PluginVarTracker::CreateArrayBuffer(uint32 size_in_bytes) {
    205   return new PluginArrayBufferVar(size_in_bytes);
    206 }
    207 
    208 ArrayBufferVar* PluginVarTracker::CreateShmArrayBuffer(
    209     uint32 size_in_bytes,
    210     base::SharedMemoryHandle handle) {
    211   return new PluginArrayBufferVar(size_in_bytes, handle);
    212 }
    213 
    214 void PluginVarTracker::PluginImplementedObjectCreated(
    215     PP_Instance instance,
    216     const PP_Var& created_var,
    217     const PPP_Class_Deprecated* ppp_class,
    218     void* ppp_class_data) {
    219   PluginImplementedVar p;
    220   p.ppp_class = ppp_class;
    221   p.instance = instance;
    222   p.plugin_object_id = created_var.value.as_id;
    223   user_data_to_plugin_[ppp_class_data] = p;
    224 
    225   // Link the user data to the object.
    226   ProxyObjectVar* object = GetVar(created_var)->AsProxyObjectVar();
    227   object->set_user_data(ppp_class_data);
    228 }
    229 
    230 void PluginVarTracker::PluginImplementedObjectDestroyed(void* user_data) {
    231   UserDataToPluginImplementedVarMap::iterator found =
    232       user_data_to_plugin_.find(user_data);
    233   if (found == user_data_to_plugin_.end()) {
    234     NOTREACHED();
    235     return;
    236   }
    237   user_data_to_plugin_.erase(found);
    238 }
    239 
    240 bool PluginVarTracker::IsPluginImplementedObjectAlive(void* user_data) {
    241   return user_data_to_plugin_.find(user_data) != user_data_to_plugin_.end();
    242 }
    243 
    244 bool PluginVarTracker::ValidatePluginObjectCall(
    245     const PPP_Class_Deprecated* ppp_class,
    246     void* user_data) {
    247   UserDataToPluginImplementedVarMap::iterator found =
    248       user_data_to_plugin_.find(user_data);
    249   if (found == user_data_to_plugin_.end())
    250     return false;
    251   return found->second.ppp_class == ppp_class;
    252 }
    253 
    254 int32 PluginVarTracker::AddVarInternal(Var* var, AddVarRefMode mode) {
    255   // Normal adding.
    256   int32 new_id = VarTracker::AddVarInternal(var, mode);
    257 
    258   // Need to add proxy objects to the host var map.
    259   ProxyObjectVar* proxy_object = var->AsProxyObjectVar();
    260   if (proxy_object) {
    261     HostVar host_var(proxy_object->dispatcher(), proxy_object->host_var_id());
    262     DCHECK(host_var_to_plugin_var_.find(host_var) ==
    263            host_var_to_plugin_var_.end());  // Adding an object twice, use
    264                                             // FindOrMakePluginVarFromHostVar.
    265     host_var_to_plugin_var_[host_var] = new_id;
    266   }
    267   return new_id;
    268 }
    269 
    270 void PluginVarTracker::TrackedObjectGettingOneRef(VarMap::const_iterator iter) {
    271   ProxyObjectVar* object = iter->second.var->AsProxyObjectVar();
    272   if (!object) {
    273     NOTREACHED();
    274     return;
    275   }
    276 
    277   DCHECK(iter->second.ref_count == 0);
    278 
    279   // Got an AddRef for an object we have no existing reference for.
    280   // We need to tell the browser we've taken a ref. This comes up when the
    281   // browser passes an object as an input param and holds a ref for us.
    282   // This must be a sync message since otherwise the "addref" will actually
    283   // occur after the return to the browser of the sync function that
    284   // presumably sent the object.
    285   SendAddRefObjectMsg(*object);
    286 }
    287 
    288 void PluginVarTracker::ObjectGettingZeroRef(VarMap::iterator iter) {
    289   ProxyObjectVar* object = iter->second.var->AsProxyObjectVar();
    290   if (!object) {
    291     NOTREACHED();
    292     return;
    293   }
    294 
    295   // Notify the host we're no longer holding our ref.
    296   DCHECK(iter->second.ref_count == 0);
    297   SendReleaseObjectMsg(*object);
    298 
    299   UserDataToPluginImplementedVarMap::iterator found =
    300       user_data_to_plugin_.find(object->user_data());
    301   if (found != user_data_to_plugin_.end()) {
    302     // This object is implemented in the plugin.
    303     if (found->second.instance == 0) {
    304       // Instance is destroyed. This means that we'll never get a Deallocate
    305       // call from the renderer and we should do so now.
    306       found->second.ppp_class->Deallocate(found->first);
    307       user_data_to_plugin_.erase(found);
    308     } else {
    309       // The plugin is releasing its last reference to an object it implements.
    310       // Clear the tracking data that links our "plugin implemented object" to
    311       // the var. If the instance is destroyed and there is no ID, we know that
    312       // we should just call Deallocate on the object data.
    313       //
    314       // See the plugin_object_id declaration for more info.
    315       found->second.plugin_object_id = 0;
    316     }
    317   }
    318 
    319   // This will optionally delete the info from live_vars_.
    320   VarTracker::ObjectGettingZeroRef(iter);
    321 }
    322 
    323 bool PluginVarTracker::DeleteObjectInfoIfNecessary(VarMap::iterator iter) {
    324   // Get the info before calling the base class's version of this function,
    325   // which may delete the object.
    326   ProxyObjectVar* object = iter->second.var->AsProxyObjectVar();
    327   HostVar host_var(object->dispatcher(), object->host_var_id());
    328 
    329   if (!VarTracker::DeleteObjectInfoIfNecessary(iter))
    330     return false;
    331 
    332   // Clean up the host var mapping.
    333   DCHECK(host_var_to_plugin_var_.find(host_var) !=
    334          host_var_to_plugin_var_.end());
    335   host_var_to_plugin_var_.erase(host_var);
    336   return true;
    337 }
    338 
    339 PP_Var PluginVarTracker::GetOrCreateObjectVarID(ProxyObjectVar* object) {
    340   // We can't use object->GetPPVar() because we don't want to affect the
    341   // refcount, so we have to add everything manually here.
    342   int32 var_id = object->GetExistingVarID();
    343   if (!var_id) {
    344     var_id = AddVarInternal(object, ADD_VAR_CREATE_WITH_NO_REFERENCE);
    345     object->AssignVarID(var_id);
    346   }
    347 
    348   PP_Var ret = { PP_VARTYPE_OBJECT };
    349   ret.value.as_id = var_id;
    350   return ret;
    351 }
    352 
    353 void PluginVarTracker::SendAddRefObjectMsg(
    354     const ProxyObjectVar& proxy_object) {
    355   int unused;
    356   if (proxy_object.dispatcher()) {
    357     proxy_object.dispatcher()->Send(new PpapiHostMsg_PPBVar_AddRefObject(
    358         API_ID_PPB_VAR_DEPRECATED, proxy_object.host_var_id(), &unused));
    359   }
    360 }
    361 
    362 void PluginVarTracker::SendReleaseObjectMsg(
    363     const ProxyObjectVar& proxy_object) {
    364   if (proxy_object.dispatcher()) {
    365     proxy_object.dispatcher()->Send(new PpapiHostMsg_PPBVar_ReleaseObject(
    366         API_ID_PPB_VAR_DEPRECATED, proxy_object.host_var_id()));
    367   }
    368 }
    369 
    370 scoped_refptr<ProxyObjectVar> PluginVarTracker::FindOrMakePluginVarFromHostVar(
    371     const PP_Var& var,
    372     PluginDispatcher* dispatcher) {
    373   DCHECK(var.type == PP_VARTYPE_OBJECT);
    374   HostVar host_var(dispatcher, var.value.as_id);
    375 
    376   HostVarToPluginVarMap::iterator found =
    377       host_var_to_plugin_var_.find(host_var);
    378   if (found == host_var_to_plugin_var_.end()) {
    379     // Create a new object.
    380     return scoped_refptr<ProxyObjectVar>(
    381         new ProxyObjectVar(dispatcher, static_cast<int32>(var.value.as_id)));
    382   }
    383 
    384   // Have this host var, look up the object.
    385   VarMap::iterator ret = live_vars_.find(found->second);
    386   DCHECK(ret != live_vars_.end());
    387 
    388   // All objects should be proxy objects.
    389   DCHECK(ret->second.var->AsProxyObjectVar());
    390   return scoped_refptr<ProxyObjectVar>(ret->second.var->AsProxyObjectVar());
    391 }
    392 
    393 int PluginVarTracker::TrackSharedMemoryHandle(PP_Instance instance,
    394                                               base::SharedMemoryHandle handle,
    395                                               uint32 size_in_bytes) {
    396   NOTREACHED();
    397   return -1;
    398 }
    399 
    400 bool PluginVarTracker::StopTrackingSharedMemoryHandle(
    401     int id,
    402     PP_Instance instance,
    403     base::SharedMemoryHandle* handle,
    404     uint32* size_in_bytes) {
    405   NOTREACHED();
    406   return false;
    407 }
    408 
    409 }  // namesace proxy
    410 }  // namespace ppapi
    411