- add sources.
[platform/framework/web/crosswalk.git] / src / ppapi / proxy / plugin_var_tracker.cc
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