• 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/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 
GetConnectionForInstance(PP_Instance instance)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 
HostVar(PluginDispatcher * d,int32 i)38 PluginVarTracker::HostVar::HostVar(PluginDispatcher* d, int32 i)
39     : dispatcher(d),
40       host_object_id(i) {
41 }
42 
operator <(const HostVar & other) const43 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 
PluginVarTracker()51 PluginVarTracker::PluginVarTracker() : VarTracker(THREAD_SAFE) {
52 }
53 
~PluginVarTracker()54 PluginVarTracker::~PluginVarTracker() {
55 }
56 
ReceiveObjectPassRef(const PP_Var & host_var,PluginDispatcher * dispatcher)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 
TrackObjectWithNoReference(const PP_Var & host_var,PluginDispatcher * dispatcher)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 
StopTrackingObjectWithNoReference(const PP_Var & plugin_var)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 
GetHostObject(const PP_Var & plugin_object) const117 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 
DispatcherForPluginObject(const PP_Var & plugin_object) const137 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 
ReleaseHostObject(PluginDispatcher * dispatcher,const PP_Var & host_object)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 
MakeResourcePPVarFromMessage(PP_Instance instance,const IPC::Message & creation_message,int pending_renderer_id,int pending_browser_id)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 
MakeResourceVar(PP_Resource pp_resource)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 
DidDeleteInstance(PP_Instance instance)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 
DidDeleteDispatcher(PluginDispatcher * dispatcher)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 
CreateArrayBuffer(uint32 size_in_bytes)268 ArrayBufferVar* PluginVarTracker::CreateArrayBuffer(uint32 size_in_bytes) {
269   return new PluginArrayBufferVar(size_in_bytes);
270 }
271 
CreateShmArrayBuffer(uint32 size_in_bytes,base::SharedMemoryHandle handle)272 ArrayBufferVar* PluginVarTracker::CreateShmArrayBuffer(
273     uint32 size_in_bytes,
274     base::SharedMemoryHandle handle) {
275   return new PluginArrayBufferVar(size_in_bytes, handle);
276 }
277 
PluginImplementedObjectCreated(PP_Instance instance,const PP_Var & created_var,const PPP_Class_Deprecated * ppp_class,void * ppp_class_data)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 
PluginImplementedObjectDestroyed(void * user_data)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 
IsPluginImplementedObjectAlive(void * user_data)304 bool PluginVarTracker::IsPluginImplementedObjectAlive(void* user_data) {
305   return user_data_to_plugin_.find(user_data) != user_data_to_plugin_.end();
306 }
307 
ValidatePluginObjectCall(const PPP_Class_Deprecated * ppp_class,void * user_data)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 
AddVarInternal(Var * var,AddVarRefMode mode)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 
TrackedObjectGettingOneRef(VarMap::const_iterator iter)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 
ObjectGettingZeroRef(VarMap::iterator iter)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 
DeleteObjectInfoIfNecessary(VarMap::iterator iter)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 
GetOrCreateObjectVarID(ProxyObjectVar * object)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 
SendAddRefObjectMsg(const ProxyObjectVar & proxy_object)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 
SendReleaseObjectMsg(const ProxyObjectVar & proxy_object)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 
FindOrMakePluginVarFromHostVar(const PP_Var & var,PluginDispatcher * dispatcher)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 
TrackSharedMemoryHandle(PP_Instance instance,base::SharedMemoryHandle handle,uint32 size_in_bytes)462 int PluginVarTracker::TrackSharedMemoryHandle(PP_Instance instance,
463                                               base::SharedMemoryHandle handle,
464                                               uint32 size_in_bytes) {
465   NOTREACHED();
466   return -1;
467 }
468 
StopTrackingSharedMemoryHandle(int id,PP_Instance instance,base::SharedMemoryHandle * handle,uint32 * size_in_bytes)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