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