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.
5 #include "ppapi/proxy/plugin_var_tracker.h"
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"
30 Connection GetConnectionForInstance(PP_Instance instance) {
31 PluginDispatcher* dispatcher = PluginDispatcher::GetForInstance(instance);
33 return Connection(PluginGlobals::Get()->GetBrowserSender(), dispatcher);
38 PluginVarTracker::HostVar::HostVar(PluginDispatcher* d, int32 i)
43 bool PluginVarTracker::HostVar::operator<(const HostVar& other) const {
44 if (dispatcher < other.dispatcher)
46 if (other.dispatcher < dispatcher)
48 return host_object_id < other.host_object_id;
51 PluginVarTracker::PluginVarTracker() : VarTracker(THREAD_SAFE) {
54 PluginVarTracker::~PluginVarTracker() {
57 PP_Var PluginVarTracker::ReceiveObjectPassRef(const PP_Var& host_var,
58 PluginDispatcher* dispatcher) {
59 CheckThreadingPreconditions();
60 DCHECK(host_var.type == PP_VARTYPE_OBJECT);
63 scoped_refptr<ProxyObjectVar> object(
64 FindOrMakePluginVarFromHostVar(host_var, dispatcher));
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());
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());
82 PP_Var PluginVarTracker::TrackObjectWithNoReference(
83 const PP_Var& host_var,
84 PluginDispatcher* dispatcher) {
85 CheckThreadingPreconditions();
86 DCHECK(host_var.type == PP_VARTYPE_OBJECT);
89 scoped_refptr<ProxyObjectVar> object(
90 FindOrMakePluginVarFromHostVar(host_var, dispatcher));
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());
96 VarInfo& info = GetLiveVar(ret)->second;
97 info.track_with_no_reference_count++;
101 void PluginVarTracker::StopTrackingObjectWithNoReference(
102 const PP_Var& plugin_var) {
103 CheckThreadingPreconditions();
104 DCHECK(plugin_var.type == PP_VARTYPE_OBJECT);
106 VarMap::iterator found = GetLiveVar(plugin_var);
107 if (found == live_vars_.end()) {
112 DCHECK(found->second.track_with_no_reference_count > 0);
113 found->second.track_with_no_reference_count--;
114 DeleteObjectInfoIfNecessary(found);
117 PP_Var PluginVarTracker::GetHostObject(const PP_Var& plugin_object) const {
118 CheckThreadingPreconditions();
119 if (plugin_object.type != PP_VARTYPE_OBJECT) {
121 return PP_MakeUndefined();
124 Var* var = GetVar(plugin_object);
125 ProxyObjectVar* object = var->AsProxyObjectVar();
128 return PP_MakeUndefined();
131 // Make a var with the host ID.
132 PP_Var ret = { PP_VARTYPE_OBJECT };
133 ret.value.as_id = object->host_var_id();
137 PluginDispatcher* PluginVarTracker::DispatcherForPluginObject(
138 const PP_Var& plugin_object) const {
139 CheckThreadingPreconditions();
140 if (plugin_object.type != PP_VARTYPE_OBJECT)
143 VarMap::const_iterator found = GetLiveVar(plugin_object);
144 if (found == live_vars_.end())
147 ProxyObjectVar* object = found->second.var->AsProxyObjectVar();
150 return object->dispatcher();
153 void PluginVarTracker::ReleaseHostObject(PluginDispatcher* dispatcher,
154 const PP_Var& host_object) {
155 CheckThreadingPreconditions();
156 DCHECK(host_object.type == PP_VARTYPE_OBJECT);
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()) {
166 // Now just release the object given the plugin var ID.
167 ReleaseVar(found->second);
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();
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),
194 file_system_type))->GetReference();
195 return MakeResourcePPVar(pp_resource);
198 NOTREACHED() << "Creation message has unexpected type "
199 << creation_message.type();
200 return PP_MakeNull();
205 ResourceVar* PluginVarTracker::MakeResourceVar(PP_Resource pp_resource) {
206 // The resource 0 returns a null resource var.
208 return new PluginResourceVar();
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.
215 return new PluginResourceVar(resource);
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.
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();
230 if (i->second.instance == instance)
231 user_data_to_delete.push_back(i->first);
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.
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);
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;
256 void PluginVarTracker::DidDeleteDispatcher(PluginDispatcher* dispatcher) {
257 for (VarMap::iterator it = live_vars_.begin();
258 it != live_vars_.end();
260 if (it->second.var.get() == NULL)
262 ProxyObjectVar* object = it->second.var->AsProxyObjectVar();
263 if (object && object->dispatcher() == dispatcher)
264 object->clear_dispatcher();
268 ArrayBufferVar* PluginVarTracker::CreateArrayBuffer(uint32 size_in_bytes) {
269 return new PluginArrayBufferVar(size_in_bytes);
272 ArrayBufferVar* PluginVarTracker::CreateShmArrayBuffer(
273 uint32 size_in_bytes,
274 base::SharedMemoryHandle handle) {
275 return new PluginArrayBufferVar(size_in_bytes, handle);
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;
289 // Link the user data to the object.
290 ProxyObjectVar* object = GetVar(created_var)->AsProxyObjectVar();
291 object->set_user_data(ppp_class_data);
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()) {
301 user_data_to_plugin_.erase(found);
304 bool PluginVarTracker::IsPluginImplementedObjectAlive(void* user_data) {
305 return user_data_to_plugin_.find(user_data) != user_data_to_plugin_.end();
308 bool PluginVarTracker::ValidatePluginObjectCall(
309 const PPP_Class_Deprecated* ppp_class,
311 UserDataToPluginImplementedVarMap::iterator found =
312 user_data_to_plugin_.find(user_data);
313 if (found == user_data_to_plugin_.end())
315 return found->second.ppp_class == ppp_class;
318 int32 PluginVarTracker::AddVarInternal(Var* var, AddVarRefMode mode) {
320 int32 new_id = VarTracker::AddVarInternal(var, mode);
322 // Need to add proxy objects to the host var map.
323 ProxyObjectVar* proxy_object = var->AsProxyObjectVar();
325 HostVar host_var(proxy_object->dispatcher(), proxy_object->host_var_id());
326 // TODO(teravest): Change to DCHECK when http://crbug.com/276347 is
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;
336 void PluginVarTracker::TrackedObjectGettingOneRef(VarMap::const_iterator iter) {
337 ProxyObjectVar* object = iter->second.var->AsProxyObjectVar();
343 DCHECK(iter->second.ref_count == 0);
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);
354 void PluginVarTracker::ObjectGettingZeroRef(VarMap::iterator iter) {
355 ProxyObjectVar* object = iter->second.var->AsProxyObjectVar();
361 // Notify the host we're no longer holding our ref.
362 DCHECK(iter->second.ref_count == 0);
363 SendReleaseObjectMsg(*object);
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);
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.
380 // See the plugin_object_id declaration for more info.
381 found->second.plugin_object_id = 0;
385 // This will optionally delete the info from live_vars_.
386 VarTracker::ObjectGettingZeroRef(iter);
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());
395 if (!VarTracker::DeleteObjectInfoIfNecessary(iter))
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);
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();
410 var_id = AddVarInternal(object, ADD_VAR_CREATE_WITH_NO_REFERENCE);
411 object->AssignVarID(var_id);
414 PP_Var ret = { PP_VARTYPE_OBJECT };
415 ret.value.as_id = var_id;
419 void PluginVarTracker::SendAddRefObjectMsg(
420 const ProxyObjectVar& proxy_object) {
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));
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()));
436 scoped_refptr<ProxyObjectVar> PluginVarTracker::FindOrMakePluginVarFromHostVar(
438 PluginDispatcher* dispatcher) {
439 DCHECK(var.type == PP_VARTYPE_OBJECT);
440 HostVar host_var(dispatcher, var.value.as_id);
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)));
450 // Have this host var, look up the object.
451 VarMap::iterator ret = live_vars_.find(found->second);
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());
457 // All objects should be proxy objects.
458 DCHECK(ret->second.var->AsProxyObjectVar());
459 return scoped_refptr<ProxyObjectVar>(ret->second.var->AsProxyObjectVar());
462 int PluginVarTracker::TrackSharedMemoryHandle(PP_Instance instance,
463 base::SharedMemoryHandle handle,
464 uint32 size_in_bytes) {
469 bool PluginVarTracker::StopTrackingSharedMemoryHandle(
471 PP_Instance instance,
472 base::SharedMemoryHandle* handle,
473 uint32* size_in_bytes) {