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