• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
GetConnectionForInstance(PP_Instance instance)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 
HostVar(PluginDispatcher * d,int32 i)40 PluginVarTracker::HostVar::HostVar(PluginDispatcher* d, int32 i)
41     : dispatcher(d),
42       host_object_id(i) {
43 }
44 
operator <(const HostVar & other) const45 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 
PluginVarTracker()53 PluginVarTracker::PluginVarTracker() : VarTracker(THREAD_SAFE) {
54 }
55 
~PluginVarTracker()56 PluginVarTracker::~PluginVarTracker() {
57 }
58 
ReceiveObjectPassRef(const PP_Var & host_var,PluginDispatcher * dispatcher)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 
TrackObjectWithNoReference(const PP_Var & host_var,PluginDispatcher * dispatcher)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 
StopTrackingObjectWithNoReference(const PP_Var & plugin_var)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 
GetHostObject(const PP_Var & plugin_object) const119 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 
DispatcherForPluginObject(const PP_Var & plugin_object) const139 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 
ReleaseHostObject(PluginDispatcher * dispatcher,const PP_Var & host_object)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 
MakeResourcePPVarFromMessage(PP_Instance instance,const IPC::Message & creation_message,int pending_renderer_id,int pending_browser_id)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 
MakeResourceVar(PP_Resource pp_resource)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 
DidDeleteInstance(PP_Instance instance)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 
DidDeleteDispatcher(PluginDispatcher * dispatcher)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 
CreateArrayBuffer(uint32 size_in_bytes)306 ArrayBufferVar* PluginVarTracker::CreateArrayBuffer(uint32 size_in_bytes) {
307   return new PluginArrayBufferVar(size_in_bytes);
308 }
309 
CreateShmArrayBuffer(uint32 size_in_bytes,base::SharedMemoryHandle handle)310 ArrayBufferVar* PluginVarTracker::CreateShmArrayBuffer(
311     uint32 size_in_bytes,
312     base::SharedMemoryHandle handle) {
313   return new PluginArrayBufferVar(size_in_bytes, handle);
314 }
315 
PluginImplementedObjectCreated(PP_Instance instance,const PP_Var & created_var,const PPP_Class_Deprecated * ppp_class,void * ppp_class_data)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 
PluginImplementedObjectDestroyed(void * user_data)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 
IsPluginImplementedObjectAlive(void * user_data)342 bool PluginVarTracker::IsPluginImplementedObjectAlive(void* user_data) {
343   return user_data_to_plugin_.find(user_data) != user_data_to_plugin_.end();
344 }
345 
ValidatePluginObjectCall(const PPP_Class_Deprecated * ppp_class,void * user_data)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 
AddVarInternal(Var * var,AddVarRefMode mode)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 
TrackedObjectGettingOneRef(VarMap::const_iterator iter)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 
ObjectGettingZeroRef(VarMap::iterator iter)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 
DeleteObjectInfoIfNecessary(VarMap::iterator iter)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 
GetOrCreateObjectVarID(ProxyObjectVar * object)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 
SendAddRefObjectMsg(const ProxyObjectVar & proxy_object)457 void PluginVarTracker::SendAddRefObjectMsg(
458     const ProxyObjectVar& proxy_object) {
459   if (proxy_object.dispatcher()) {
460     proxy_object.dispatcher()->Send(new PpapiHostMsg_PPBVar_AddRefObject(
461         API_ID_PPB_VAR_DEPRECATED, proxy_object.host_var_id()));
462   }
463 }
464 
SendReleaseObjectMsg(const ProxyObjectVar & proxy_object)465 void PluginVarTracker::SendReleaseObjectMsg(
466     const ProxyObjectVar& proxy_object) {
467   if (proxy_object.dispatcher()) {
468     proxy_object.dispatcher()->Send(new PpapiHostMsg_PPBVar_ReleaseObject(
469         API_ID_PPB_VAR_DEPRECATED, proxy_object.host_var_id()));
470   }
471 }
472 
FindOrMakePluginVarFromHostVar(const PP_Var & var,PluginDispatcher * dispatcher)473 scoped_refptr<ProxyObjectVar> PluginVarTracker::FindOrMakePluginVarFromHostVar(
474     const PP_Var& var,
475     PluginDispatcher* dispatcher) {
476   DCHECK(var.type == PP_VARTYPE_OBJECT);
477   HostVar host_var(dispatcher, var.value.as_id);
478 
479   HostVarToPluginVarMap::iterator found =
480       host_var_to_plugin_var_.find(host_var);
481   if (found == host_var_to_plugin_var_.end()) {
482     // Create a new object.
483     return scoped_refptr<ProxyObjectVar>(
484         new ProxyObjectVar(dispatcher, static_cast<int32>(var.value.as_id)));
485   }
486 
487   // Have this host var, look up the object.
488   VarMap::iterator ret = live_vars_.find(found->second);
489 
490   // We CHECK here because we currently don't fall back sanely.
491   // This may be involved in a NULL dereference. http://crbug.com/276347
492   CHECK(ret != live_vars_.end());
493 
494   // All objects should be proxy objects.
495   DCHECK(ret->second.var->AsProxyObjectVar());
496   return scoped_refptr<ProxyObjectVar>(ret->second.var->AsProxyObjectVar());
497 }
498 
TrackSharedMemoryHandle(PP_Instance instance,base::SharedMemoryHandle handle,uint32 size_in_bytes)499 int PluginVarTracker::TrackSharedMemoryHandle(PP_Instance instance,
500                                               base::SharedMemoryHandle handle,
501                                               uint32 size_in_bytes) {
502   NOTREACHED();
503   return -1;
504 }
505 
StopTrackingSharedMemoryHandle(int id,PP_Instance instance,base::SharedMemoryHandle * handle,uint32 * size_in_bytes)506 bool PluginVarTracker::StopTrackingSharedMemoryHandle(
507     int id,
508     PP_Instance instance,
509     base::SharedMemoryHandle* handle,
510     uint32* size_in_bytes) {
511   NOTREACHED();
512   return false;
513 }
514 
515 }  // namesace proxy
516 }  // namespace ppapi
517