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 "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/proxy_lock.h"
17 #include "ppapi/shared_impl/var.h"
22 PluginVarTracker::HostVar::HostVar(PluginDispatcher
* d
, int32 i
)
27 bool PluginVarTracker::HostVar::operator<(const HostVar
& other
) const {
28 if (dispatcher
< other
.dispatcher
)
30 if (other
.dispatcher
< dispatcher
)
32 return host_object_id
< other
.host_object_id
;
35 PluginVarTracker::PluginVarTracker() : VarTracker(THREAD_SAFE
) {
38 PluginVarTracker::~PluginVarTracker() {
41 PP_Var
PluginVarTracker::ReceiveObjectPassRef(const PP_Var
& host_var
,
42 PluginDispatcher
* dispatcher
) {
43 CheckThreadingPreconditions();
44 DCHECK(host_var
.type
== PP_VARTYPE_OBJECT
);
47 scoped_refptr
<ProxyObjectVar
> object(
48 FindOrMakePluginVarFromHostVar(host_var
, dispatcher
));
50 // Actually create the PP_Var, this will add all the tracking info but not
51 // adjust any refcounts.
52 PP_Var ret
= GetOrCreateObjectVarID(object
.get());
54 VarInfo
& info
= GetLiveVar(ret
)->second
;
55 if (info
.ref_count
> 0) {
56 // We already had a reference to it before. That means the renderer now has
57 // two references on our behalf. We want to transfer that extra reference
58 // to our list. This means we addref in the plugin, and release the extra
59 // one in the renderer.
60 SendReleaseObjectMsg(*object
.get());
66 PP_Var
PluginVarTracker::TrackObjectWithNoReference(
67 const PP_Var
& host_var
,
68 PluginDispatcher
* dispatcher
) {
69 CheckThreadingPreconditions();
70 DCHECK(host_var
.type
== PP_VARTYPE_OBJECT
);
73 scoped_refptr
<ProxyObjectVar
> object(
74 FindOrMakePluginVarFromHostVar(host_var
, dispatcher
));
76 // Actually create the PP_Var, this will add all the tracking info but not
77 // adjust any refcounts.
78 PP_Var ret
= GetOrCreateObjectVarID(object
.get());
80 VarInfo
& info
= GetLiveVar(ret
)->second
;
81 info
.track_with_no_reference_count
++;
85 void PluginVarTracker::StopTrackingObjectWithNoReference(
86 const PP_Var
& plugin_var
) {
87 CheckThreadingPreconditions();
88 DCHECK(plugin_var
.type
== PP_VARTYPE_OBJECT
);
90 VarMap::iterator found
= GetLiveVar(plugin_var
);
91 if (found
== live_vars_
.end()) {
96 DCHECK(found
->second
.track_with_no_reference_count
> 0);
97 found
->second
.track_with_no_reference_count
--;
98 DeleteObjectInfoIfNecessary(found
);
101 PP_Var
PluginVarTracker::GetHostObject(const PP_Var
& plugin_object
) const {
102 CheckThreadingPreconditions();
103 if (plugin_object
.type
!= PP_VARTYPE_OBJECT
) {
105 return PP_MakeUndefined();
108 Var
* var
= GetVar(plugin_object
);
109 ProxyObjectVar
* object
= var
->AsProxyObjectVar();
112 return PP_MakeUndefined();
115 // Make a var with the host ID.
116 PP_Var ret
= { PP_VARTYPE_OBJECT
};
117 ret
.value
.as_id
= object
->host_var_id();
121 PluginDispatcher
* PluginVarTracker::DispatcherForPluginObject(
122 const PP_Var
& plugin_object
) const {
123 CheckThreadingPreconditions();
124 if (plugin_object
.type
!= PP_VARTYPE_OBJECT
)
127 VarMap::const_iterator found
= GetLiveVar(plugin_object
);
128 if (found
== live_vars_
.end())
131 ProxyObjectVar
* object
= found
->second
.var
->AsProxyObjectVar();
134 return object
->dispatcher();
137 void PluginVarTracker::ReleaseHostObject(PluginDispatcher
* dispatcher
,
138 const PP_Var
& host_object
) {
139 CheckThreadingPreconditions();
140 DCHECK(host_object
.type
== PP_VARTYPE_OBJECT
);
142 // Convert the host object to a normal var valid in the plugin.
143 HostVarToPluginVarMap::iterator found
= host_var_to_plugin_var_
.find(
144 HostVar(dispatcher
, static_cast<int32
>(host_object
.value
.as_id
)));
145 if (found
== host_var_to_plugin_var_
.end()) {
150 // Now just release the object given the plugin var ID.
151 ReleaseVar(found
->second
);
154 void PluginVarTracker::DidDeleteInstance(PP_Instance instance
) {
155 // Calling the destructors on plugin objects may in turn release other
156 // objects which will mutate the map out from under us. So do a two-step
157 // process of identifying the ones to delete, and then delete them.
159 // See the comment above user_data_to_plugin_ in the header file. We assume
160 // there aren't that many objects so a brute-force search is reasonable.
161 std::vector
<void*> user_data_to_delete
;
162 for (UserDataToPluginImplementedVarMap::const_iterator i
=
163 user_data_to_plugin_
.begin();
164 i
!= user_data_to_plugin_
.end();
166 if (i
->second
.instance
== instance
)
167 user_data_to_delete
.push_back(i
->first
);
170 for (size_t i
= 0; i
< user_data_to_delete
.size(); i
++) {
171 UserDataToPluginImplementedVarMap::iterator found
=
172 user_data_to_plugin_
.find(user_data_to_delete
[i
]);
173 if (found
== user_data_to_plugin_
.end())
174 continue; // Object removed from list while we were iterating.
176 if (!found
->second
.plugin_object_id
) {
177 // This object is for the freed instance and the plugin is not holding
178 // any references to it. Deallocate immediately.
179 CallWhileUnlocked(found
->second
.ppp_class
->Deallocate
, found
->first
);
180 user_data_to_plugin_
.erase(found
);
182 // The plugin is holding refs to this object. We don't want to call
183 // Deallocate since the plugin may be depending on those refs to keep
184 // its data alive. To avoid crashes in this case, just clear out the
185 // instance to mark it and continue. When the plugin refs go to 0,
186 // we'll notice there is no instance and call Deallocate.
187 found
->second
.instance
= 0;
192 void PluginVarTracker::DidDeleteDispatcher(PluginDispatcher
* dispatcher
) {
193 for (VarMap::iterator it
= live_vars_
.begin();
194 it
!= live_vars_
.end();
196 if (it
->second
.var
.get() == NULL
)
198 ProxyObjectVar
* object
= it
->second
.var
->AsProxyObjectVar();
199 if (object
&& object
->dispatcher() == dispatcher
)
200 object
->clear_dispatcher();
204 ArrayBufferVar
* PluginVarTracker::CreateArrayBuffer(uint32 size_in_bytes
) {
205 return new PluginArrayBufferVar(size_in_bytes
);
208 ArrayBufferVar
* PluginVarTracker::CreateShmArrayBuffer(
209 uint32 size_in_bytes
,
210 base::SharedMemoryHandle handle
) {
211 return new PluginArrayBufferVar(size_in_bytes
, handle
);
214 void PluginVarTracker::PluginImplementedObjectCreated(
215 PP_Instance instance
,
216 const PP_Var
& created_var
,
217 const PPP_Class_Deprecated
* ppp_class
,
218 void* ppp_class_data
) {
219 PluginImplementedVar p
;
220 p
.ppp_class
= ppp_class
;
221 p
.instance
= instance
;
222 p
.plugin_object_id
= created_var
.value
.as_id
;
223 user_data_to_plugin_
[ppp_class_data
] = p
;
225 // Link the user data to the object.
226 ProxyObjectVar
* object
= GetVar(created_var
)->AsProxyObjectVar();
227 object
->set_user_data(ppp_class_data
);
230 void PluginVarTracker::PluginImplementedObjectDestroyed(void* user_data
) {
231 UserDataToPluginImplementedVarMap::iterator found
=
232 user_data_to_plugin_
.find(user_data
);
233 if (found
== user_data_to_plugin_
.end()) {
237 user_data_to_plugin_
.erase(found
);
240 bool PluginVarTracker::IsPluginImplementedObjectAlive(void* user_data
) {
241 return user_data_to_plugin_
.find(user_data
) != user_data_to_plugin_
.end();
244 bool PluginVarTracker::ValidatePluginObjectCall(
245 const PPP_Class_Deprecated
* ppp_class
,
247 UserDataToPluginImplementedVarMap::iterator found
=
248 user_data_to_plugin_
.find(user_data
);
249 if (found
== user_data_to_plugin_
.end())
251 return found
->second
.ppp_class
== ppp_class
;
254 int32
PluginVarTracker::AddVarInternal(Var
* var
, AddVarRefMode mode
) {
256 int32 new_id
= VarTracker::AddVarInternal(var
, mode
);
258 // Need to add proxy objects to the host var map.
259 ProxyObjectVar
* proxy_object
= var
->AsProxyObjectVar();
261 HostVar
host_var(proxy_object
->dispatcher(), proxy_object
->host_var_id());
262 DCHECK(host_var_to_plugin_var_
.find(host_var
) ==
263 host_var_to_plugin_var_
.end()); // Adding an object twice, use
264 // FindOrMakePluginVarFromHostVar.
265 host_var_to_plugin_var_
[host_var
] = new_id
;
270 void PluginVarTracker::TrackedObjectGettingOneRef(VarMap::const_iterator iter
) {
271 ProxyObjectVar
* object
= iter
->second
.var
->AsProxyObjectVar();
277 DCHECK(iter
->second
.ref_count
== 0);
279 // Got an AddRef for an object we have no existing reference for.
280 // We need to tell the browser we've taken a ref. This comes up when the
281 // browser passes an object as an input param and holds a ref for us.
282 // This must be a sync message since otherwise the "addref" will actually
283 // occur after the return to the browser of the sync function that
284 // presumably sent the object.
285 SendAddRefObjectMsg(*object
);
288 void PluginVarTracker::ObjectGettingZeroRef(VarMap::iterator iter
) {
289 ProxyObjectVar
* object
= iter
->second
.var
->AsProxyObjectVar();
295 // Notify the host we're no longer holding our ref.
296 DCHECK(iter
->second
.ref_count
== 0);
297 SendReleaseObjectMsg(*object
);
299 UserDataToPluginImplementedVarMap::iterator found
=
300 user_data_to_plugin_
.find(object
->user_data());
301 if (found
!= user_data_to_plugin_
.end()) {
302 // This object is implemented in the plugin.
303 if (found
->second
.instance
== 0) {
304 // Instance is destroyed. This means that we'll never get a Deallocate
305 // call from the renderer and we should do so now.
306 found
->second
.ppp_class
->Deallocate(found
->first
);
307 user_data_to_plugin_
.erase(found
);
309 // The plugin is releasing its last reference to an object it implements.
310 // Clear the tracking data that links our "plugin implemented object" to
311 // the var. If the instance is destroyed and there is no ID, we know that
312 // we should just call Deallocate on the object data.
314 // See the plugin_object_id declaration for more info.
315 found
->second
.plugin_object_id
= 0;
319 // This will optionally delete the info from live_vars_.
320 VarTracker::ObjectGettingZeroRef(iter
);
323 bool PluginVarTracker::DeleteObjectInfoIfNecessary(VarMap::iterator iter
) {
324 // Get the info before calling the base class's version of this function,
325 // which may delete the object.
326 ProxyObjectVar
* object
= iter
->second
.var
->AsProxyObjectVar();
327 HostVar
host_var(object
->dispatcher(), object
->host_var_id());
329 if (!VarTracker::DeleteObjectInfoIfNecessary(iter
))
332 // Clean up the host var mapping.
333 DCHECK(host_var_to_plugin_var_
.find(host_var
) !=
334 host_var_to_plugin_var_
.end());
335 host_var_to_plugin_var_
.erase(host_var
);
339 PP_Var
PluginVarTracker::GetOrCreateObjectVarID(ProxyObjectVar
* object
) {
340 // We can't use object->GetPPVar() because we don't want to affect the
341 // refcount, so we have to add everything manually here.
342 int32 var_id
= object
->GetExistingVarID();
344 var_id
= AddVarInternal(object
, ADD_VAR_CREATE_WITH_NO_REFERENCE
);
345 object
->AssignVarID(var_id
);
348 PP_Var ret
= { PP_VARTYPE_OBJECT
};
349 ret
.value
.as_id
= var_id
;
353 void PluginVarTracker::SendAddRefObjectMsg(
354 const ProxyObjectVar
& proxy_object
) {
356 if (proxy_object
.dispatcher()) {
357 proxy_object
.dispatcher()->Send(new PpapiHostMsg_PPBVar_AddRefObject(
358 API_ID_PPB_VAR_DEPRECATED
, proxy_object
.host_var_id(), &unused
));
362 void PluginVarTracker::SendReleaseObjectMsg(
363 const ProxyObjectVar
& proxy_object
) {
364 if (proxy_object
.dispatcher()) {
365 proxy_object
.dispatcher()->Send(new PpapiHostMsg_PPBVar_ReleaseObject(
366 API_ID_PPB_VAR_DEPRECATED
, proxy_object
.host_var_id()));
370 scoped_refptr
<ProxyObjectVar
> PluginVarTracker::FindOrMakePluginVarFromHostVar(
372 PluginDispatcher
* dispatcher
) {
373 DCHECK(var
.type
== PP_VARTYPE_OBJECT
);
374 HostVar
host_var(dispatcher
, var
.value
.as_id
);
376 HostVarToPluginVarMap::iterator found
=
377 host_var_to_plugin_var_
.find(host_var
);
378 if (found
== host_var_to_plugin_var_
.end()) {
379 // Create a new object.
380 return scoped_refptr
<ProxyObjectVar
>(
381 new ProxyObjectVar(dispatcher
, static_cast<int32
>(var
.value
.as_id
)));
384 // Have this host var, look up the object.
385 VarMap::iterator ret
= live_vars_
.find(found
->second
);
386 DCHECK(ret
!= live_vars_
.end());
388 // All objects should be proxy objects.
389 DCHECK(ret
->second
.var
->AsProxyObjectVar());
390 return scoped_refptr
<ProxyObjectVar
>(ret
->second
.var
->AsProxyObjectVar());
393 int PluginVarTracker::TrackSharedMemoryHandle(PP_Instance instance
,
394 base::SharedMemoryHandle handle
,
395 uint32 size_in_bytes
) {
400 bool PluginVarTracker::StopTrackingSharedMemoryHandle(
402 PP_Instance instance
,
403 base::SharedMemoryHandle
* handle
,
404 uint32
* size_in_bytes
) {