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