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