plugin_var_tracker.cc revision c2e0dbddbe15c98d52c4786dac06cb8952a8ae6d
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 (size_t i = 0; i < live_vars_.size(); ++i) {
193    if (live_vars_[i].var.get() == NULL)
194      continue;
195    ProxyObjectVar* object = live_vars_[i].var->AsProxyObjectVar();
196    if (object && object->dispatcher() == dispatcher)
197      object->clear_dispatcher();
198  }
199}
200
201ArrayBufferVar* PluginVarTracker::CreateArrayBuffer(uint32 size_in_bytes) {
202  return new PluginArrayBufferVar(size_in_bytes);
203}
204
205ArrayBufferVar* PluginVarTracker::CreateShmArrayBuffer(
206    uint32 size_in_bytes,
207    base::SharedMemoryHandle handle) {
208  return new PluginArrayBufferVar(size_in_bytes, handle);
209}
210
211void PluginVarTracker::PluginImplementedObjectCreated(
212    PP_Instance instance,
213    const PP_Var& created_var,
214    const PPP_Class_Deprecated* ppp_class,
215    void* ppp_class_data) {
216  PluginImplementedVar p;
217  p.ppp_class = ppp_class;
218  p.instance = instance;
219  p.plugin_object_id = created_var.value.as_id;
220  user_data_to_plugin_[ppp_class_data] = p;
221
222  // Link the user data to the object.
223  ProxyObjectVar* object = GetVar(created_var)->AsProxyObjectVar();
224  object->set_user_data(ppp_class_data);
225}
226
227void PluginVarTracker::PluginImplementedObjectDestroyed(void* user_data) {
228  UserDataToPluginImplementedVarMap::iterator found =
229      user_data_to_plugin_.find(user_data);
230  if (found == user_data_to_plugin_.end()) {
231    NOTREACHED();
232    return;
233  }
234  user_data_to_plugin_.erase(found);
235}
236
237bool PluginVarTracker::IsPluginImplementedObjectAlive(void* user_data) {
238  return user_data_to_plugin_.find(user_data) != user_data_to_plugin_.end();
239}
240
241bool PluginVarTracker::ValidatePluginObjectCall(
242    const PPP_Class_Deprecated* ppp_class,
243    void* user_data) {
244  UserDataToPluginImplementedVarMap::iterator found =
245      user_data_to_plugin_.find(user_data);
246  if (found == user_data_to_plugin_.end())
247    return false;
248  return found->second.ppp_class == ppp_class;
249}
250
251int32 PluginVarTracker::AddVarInternal(Var* var, AddVarRefMode mode) {
252  // Normal adding.
253  int32 new_id = VarTracker::AddVarInternal(var, mode);
254
255  // Need to add proxy objects to the host var map.
256  ProxyObjectVar* proxy_object = var->AsProxyObjectVar();
257  if (proxy_object) {
258    HostVar host_var(proxy_object->dispatcher(), proxy_object->host_var_id());
259    DCHECK(host_var_to_plugin_var_.find(host_var) ==
260           host_var_to_plugin_var_.end());  // Adding an object twice, use
261                                            // FindOrMakePluginVarFromHostVar.
262    host_var_to_plugin_var_[host_var] = new_id;
263  }
264  return new_id;
265}
266
267void PluginVarTracker::TrackedObjectGettingOneRef(VarMap::const_iterator iter) {
268  ProxyObjectVar* object = iter->second.var->AsProxyObjectVar();
269  if (!object) {
270    NOTREACHED();
271    return;
272  }
273
274  DCHECK(iter->second.ref_count == 0);
275
276  // Got an AddRef for an object we have no existing reference for.
277  // We need to tell the browser we've taken a ref. This comes up when the
278  // browser passes an object as an input param and holds a ref for us.
279  // This must be a sync message since otherwise the "addref" will actually
280  // occur after the return to the browser of the sync function that
281  // presumably sent the object.
282  SendAddRefObjectMsg(*object);
283}
284
285void PluginVarTracker::ObjectGettingZeroRef(VarMap::iterator iter) {
286  ProxyObjectVar* object = iter->second.var->AsProxyObjectVar();
287  if (!object) {
288    NOTREACHED();
289    return;
290  }
291
292  // Notify the host we're no longer holding our ref.
293  DCHECK(iter->second.ref_count == 0);
294  SendReleaseObjectMsg(*object);
295
296  UserDataToPluginImplementedVarMap::iterator found =
297      user_data_to_plugin_.find(object->user_data());
298  if (found != user_data_to_plugin_.end()) {
299    // This object is implemented in the plugin.
300    if (found->second.instance == 0) {
301      // Instance is destroyed. This means that we'll never get a Deallocate
302      // call from the renderer and we should do so now.
303      found->second.ppp_class->Deallocate(found->first);
304      user_data_to_plugin_.erase(found);
305    } else {
306      // The plugin is releasing its last reference to an object it implements.
307      // Clear the tracking data that links our "plugin implemented object" to
308      // the var. If the instance is destroyed and there is no ID, we know that
309      // we should just call Deallocate on the object data.
310      //
311      // See the plugin_object_id declaration for more info.
312      found->second.plugin_object_id = 0;
313    }
314  }
315
316  // This will optionally delete the info from live_vars_.
317  VarTracker::ObjectGettingZeroRef(iter);
318}
319
320bool PluginVarTracker::DeleteObjectInfoIfNecessary(VarMap::iterator iter) {
321  // Get the info before calling the base class's version of this function,
322  // which may delete the object.
323  ProxyObjectVar* object = iter->second.var->AsProxyObjectVar();
324  HostVar host_var(object->dispatcher(), object->host_var_id());
325
326  if (!VarTracker::DeleteObjectInfoIfNecessary(iter))
327    return false;
328
329  // Clean up the host var mapping.
330  DCHECK(host_var_to_plugin_var_.find(host_var) !=
331         host_var_to_plugin_var_.end());
332  host_var_to_plugin_var_.erase(host_var);
333  return true;
334}
335
336PP_Var PluginVarTracker::GetOrCreateObjectVarID(ProxyObjectVar* object) {
337  // We can't use object->GetPPVar() because we don't want to affect the
338  // refcount, so we have to add everything manually here.
339  int32 var_id = object->GetExistingVarID();
340  if (!var_id) {
341    var_id = AddVarInternal(object, ADD_VAR_CREATE_WITH_NO_REFERENCE);
342    object->AssignVarID(var_id);
343  }
344
345  PP_Var ret = { PP_VARTYPE_OBJECT };
346  ret.value.as_id = var_id;
347  return ret;
348}
349
350void PluginVarTracker::SendAddRefObjectMsg(
351    const ProxyObjectVar& proxy_object) {
352  int unused;
353  if (proxy_object.dispatcher()) {
354    proxy_object.dispatcher()->Send(new PpapiHostMsg_PPBVar_AddRefObject(
355        API_ID_PPB_VAR_DEPRECATED, proxy_object.host_var_id(), &unused));
356  }
357}
358
359void PluginVarTracker::SendReleaseObjectMsg(
360    const ProxyObjectVar& proxy_object) {
361  if (proxy_object.dispatcher()) {
362    proxy_object.dispatcher()->Send(new PpapiHostMsg_PPBVar_ReleaseObject(
363        API_ID_PPB_VAR_DEPRECATED, proxy_object.host_var_id()));
364  }
365}
366
367scoped_refptr<ProxyObjectVar> PluginVarTracker::FindOrMakePluginVarFromHostVar(
368    const PP_Var& var,
369    PluginDispatcher* dispatcher) {
370  DCHECK(var.type == PP_VARTYPE_OBJECT);
371  HostVar host_var(dispatcher, var.value.as_id);
372
373  HostVarToPluginVarMap::iterator found =
374      host_var_to_plugin_var_.find(host_var);
375  if (found == host_var_to_plugin_var_.end()) {
376    // Create a new object.
377    return scoped_refptr<ProxyObjectVar>(
378        new ProxyObjectVar(dispatcher, static_cast<int32>(var.value.as_id)));
379  }
380
381  // Have this host var, look up the object.
382  VarMap::iterator ret = live_vars_.find(found->second);
383  DCHECK(ret != live_vars_.end());
384
385  // All objects should be proxy objects.
386  DCHECK(ret->second.var->AsProxyObjectVar());
387  return scoped_refptr<ProxyObjectVar>(ret->second.var->AsProxyObjectVar());
388}
389
390int PluginVarTracker::TrackSharedMemoryHandle(PP_Instance instance,
391                                              base::SharedMemoryHandle handle,
392                                              uint32 size_in_bytes) {
393  NOTREACHED();
394  return -1;
395}
396
397bool PluginVarTracker::StopTrackingSharedMemoryHandle(
398    int id,
399    PP_Instance instance,
400    base::SharedMemoryHandle* handle,
401    uint32* size_in_bytes) {
402  NOTREACHED();
403  return false;
404}
405
406}  // namesace proxy
407}  // namespace ppapi
408