plugin_var_tracker.cc revision 0f1bc08d4cfcc34181b0b5cbf065c40f687bf740
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
25namespace ppapi {
26namespace proxy {
27
28namespace {
29
30Connection 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
38PluginVarTracker::HostVar::HostVar(PluginDispatcher* d, int32 i)
39    : dispatcher(d),
40      host_object_id(i) {
41}
42
43bool 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
51PluginVarTracker::PluginVarTracker() : VarTracker(THREAD_SAFE) {
52}
53
54PluginVarTracker::~PluginVarTracker() {
55}
56
57PP_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
82PP_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
101void 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
117PP_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
137PluginDispatcher* 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
153void 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
170PP_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
205ResourceVar* 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
218void 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
256void 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
268ArrayBufferVar* PluginVarTracker::CreateArrayBuffer(uint32 size_in_bytes) {
269  return new PluginArrayBufferVar(size_in_bytes);
270}
271
272ArrayBufferVar* PluginVarTracker::CreateShmArrayBuffer(
273    uint32 size_in_bytes,
274    base::SharedMemoryHandle handle) {
275  return new PluginArrayBufferVar(size_in_bytes, handle);
276}
277
278void 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
294void 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
304bool PluginVarTracker::IsPluginImplementedObjectAlive(void* user_data) {
305  return user_data_to_plugin_.find(user_data) != user_data_to_plugin_.end();
306}
307
308bool 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
318int32 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
336void 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
354void 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
389bool 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
405PP_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
419void 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
428void 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
436scoped_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
462int PluginVarTracker::TrackSharedMemoryHandle(PP_Instance instance,
463                                              base::SharedMemoryHandle handle,
464                                              uint32 size_in_bytes) {
465  NOTREACHED();
466  return -1;
467}
468
469bool 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