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/var.h"
21 PluginVarTracker::HostVar::HostVar(PluginDispatcher
* d
, int32 i
)
26 bool PluginVarTracker::HostVar::operator<(const HostVar
& other
) const {
27 if (dispatcher
< other
.dispatcher
)
29 if (other
.dispatcher
< dispatcher
)
31 return host_object_id
< other
.host_object_id
;
34 PluginVarTracker::PluginVarTracker() {
37 PluginVarTracker::~PluginVarTracker() {
40 PP_Var
PluginVarTracker::ReceiveObjectPassRef(const PP_Var
& host_var
,
41 PluginDispatcher
* dispatcher
) {
42 DCHECK(CalledOnValidThread());
43 DCHECK(host_var
.type
== PP_VARTYPE_OBJECT
);
46 scoped_refptr
<ProxyObjectVar
> object(
47 FindOrMakePluginVarFromHostVar(host_var
, dispatcher
));
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());
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
);
65 PP_Var
PluginVarTracker::TrackObjectWithNoReference(
66 const PP_Var
& host_var
,
67 PluginDispatcher
* dispatcher
) {
68 DCHECK(CalledOnValidThread());
69 DCHECK(host_var
.type
== PP_VARTYPE_OBJECT
);
72 scoped_refptr
<ProxyObjectVar
> object(
73 FindOrMakePluginVarFromHostVar(host_var
, dispatcher
));
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());
79 VarInfo
& info
= GetLiveVar(ret
)->second
;
80 info
.track_with_no_reference_count
++;
84 void PluginVarTracker::StopTrackingObjectWithNoReference(
85 const PP_Var
& plugin_var
) {
86 DCHECK(CalledOnValidThread());
87 DCHECK(plugin_var
.type
== PP_VARTYPE_OBJECT
);
89 VarMap::iterator found
= GetLiveVar(plugin_var
);
90 if (found
== live_vars_
.end()) {
95 DCHECK(found
->second
.track_with_no_reference_count
> 0);
96 found
->second
.track_with_no_reference_count
--;
97 DeleteObjectInfoIfNecessary(found
);
100 PP_Var
PluginVarTracker::GetHostObject(const PP_Var
& plugin_object
) const {
101 DCHECK(CalledOnValidThread());
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 DCHECK(CalledOnValidThread());
125 if (plugin_object
.type
!= PP_VARTYPE_OBJECT
)
128 VarMap::const_iterator found
= GetLiveVar(plugin_object
);
129 if (found
== live_vars_
.end())
132 ProxyObjectVar
* object
= found
->second
.var
->AsProxyObjectVar();
135 return object
->dispatcher();
138 void PluginVarTracker::ReleaseHostObject(PluginDispatcher
* dispatcher
,
139 const PP_Var
& host_object
) {
140 DCHECK(CalledOnValidThread());
141 DCHECK(host_object
.type
== PP_VARTYPE_OBJECT
);
143 // Convert the host object to a normal var valid in the plugin.
144 HostVarToPluginVarMap::iterator found
= host_var_to_plugin_var_
.find(
145 HostVar(dispatcher
, static_cast<int32
>(host_object
.value
.as_id
)));
146 if (found
== host_var_to_plugin_var_
.end()) {
151 // Now just release the object given the plugin var ID.
152 ReleaseVar(found
->second
);
155 void PluginVarTracker::DidDeleteInstance(PP_Instance instance
) {
156 // Calling the destructors on plugin objects may in turn release other
157 // objects which will mutate the map out from under us. So do a two-step
158 // process of identifying the ones to delete, and then delete them.
160 // See the comment above user_data_to_plugin_ in the header file. We assume
161 // there aren't that many objects so a brute-force search is reasonable.
162 std::vector
<void*> user_data_to_delete
;
163 for (UserDataToPluginImplementedVarMap::const_iterator i
=
164 user_data_to_plugin_
.begin();
165 i
!= user_data_to_plugin_
.end();
167 if (i
->second
.instance
== instance
)
168 user_data_to_delete
.push_back(i
->first
);
171 for (size_t i
= 0; i
< user_data_to_delete
.size(); i
++) {
172 UserDataToPluginImplementedVarMap::iterator found
=
173 user_data_to_plugin_
.find(user_data_to_delete
[i
]);
174 if (found
== user_data_to_plugin_
.end())
175 continue; // Object removed from list while we were iterating.
177 if (!found
->second
.plugin_object_id
) {
178 // This object is for the freed instance and the plugin is not holding
179 // any references to it. Deallocate immediately.
180 found
->second
.ppp_class
->Deallocate(found
->first
);
181 user_data_to_plugin_
.erase(found
);
183 // The plugin is holding refs to this object. We don't want to call
184 // Deallocate since the plugin may be depending on those refs to keep
185 // its data alive. To avoid crashes in this case, just clear out the
186 // instance to mark it and continue. When the plugin refs go to 0,
187 // we'll notice there is no instance and call Deallocate.
188 found
->second
.instance
= 0;
193 ArrayBufferVar
* PluginVarTracker::CreateArrayBuffer(uint32 size_in_bytes
) {
194 return new PluginArrayBufferVar(size_in_bytes
);
197 void PluginVarTracker::PluginImplementedObjectCreated(
198 PP_Instance instance
,
199 const PP_Var
& created_var
,
200 const PPP_Class_Deprecated
* ppp_class
,
201 void* ppp_class_data
) {
202 PluginImplementedVar p
;
203 p
.ppp_class
= ppp_class
;
204 p
.instance
= instance
;
205 p
.plugin_object_id
= created_var
.value
.as_id
;
206 user_data_to_plugin_
[ppp_class_data
] = p
;
208 // Link the user data to the object.
209 ProxyObjectVar
* object
= GetVar(created_var
)->AsProxyObjectVar();
210 object
->set_user_data(ppp_class_data
);
213 void PluginVarTracker::PluginImplementedObjectDestroyed(void* user_data
) {
214 UserDataToPluginImplementedVarMap::iterator found
=
215 user_data_to_plugin_
.find(user_data
);
216 if (found
== user_data_to_plugin_
.end()) {
220 user_data_to_plugin_
.erase(found
);
223 bool PluginVarTracker::IsPluginImplementedObjectAlive(void* user_data
) {
224 return user_data_to_plugin_
.find(user_data
) != user_data_to_plugin_
.end();
227 bool PluginVarTracker::ValidatePluginObjectCall(
228 const PPP_Class_Deprecated
* ppp_class
,
230 UserDataToPluginImplementedVarMap::iterator found
=
231 user_data_to_plugin_
.find(user_data
);
232 if (found
== user_data_to_plugin_
.end())
234 return found
->second
.ppp_class
== ppp_class
;
237 int32
PluginVarTracker::AddVarInternal(Var
* var
, AddVarRefMode mode
) {
239 int32 new_id
= VarTracker::AddVarInternal(var
, mode
);
241 // Need to add proxy objects to the host var map.
242 ProxyObjectVar
* proxy_object
= var
->AsProxyObjectVar();
244 HostVar
host_var(proxy_object
->dispatcher(), proxy_object
->host_var_id());
245 DCHECK(host_var_to_plugin_var_
.find(host_var
) ==
246 host_var_to_plugin_var_
.end()); // Adding an object twice, use
247 // FindOrMakePluginVarFromHostVar.
248 host_var_to_plugin_var_
[host_var
] = new_id
;
253 void PluginVarTracker::TrackedObjectGettingOneRef(VarMap::const_iterator iter
) {
254 ProxyObjectVar
* object
= iter
->second
.var
->AsProxyObjectVar();
260 DCHECK(iter
->second
.ref_count
== 0);
262 // Got an AddRef for an object we have no existing reference for.
263 // We need to tell the browser we've taken a ref. This comes up when the
264 // browser passes an object as an input param and holds a ref for us.
265 // This must be a sync message since otherwise the "addref" will actually
266 // occur after the return to the browser of the sync function that
267 // presumably sent the object.
268 SendAddRefObjectMsg(*object
);
271 void PluginVarTracker::ObjectGettingZeroRef(VarMap::iterator iter
) {
272 ProxyObjectVar
* object
= iter
->second
.var
->AsProxyObjectVar();
278 // Notify the host we're no longer holding our ref.
279 DCHECK(iter
->second
.ref_count
== 0);
280 SendReleaseObjectMsg(*object
);
282 UserDataToPluginImplementedVarMap::iterator found
=
283 user_data_to_plugin_
.find(object
->user_data());
284 if (found
!= user_data_to_plugin_
.end()) {
285 // This object is implemented in the plugin.
286 if (found
->second
.instance
== 0) {
287 // Instance is destroyed. This means that we'll never get a Deallocate
288 // call from the renderer and we should do so now.
289 found
->second
.ppp_class
->Deallocate(found
->first
);
290 user_data_to_plugin_
.erase(found
);
292 // The plugin is releasing its last reference to an object it implements.
293 // Clear the tracking data that links our "plugin implemented object" to
294 // the var. If the instance is destroyed and there is no ID, we know that
295 // we should just call Deallocate on the object data.
297 // See the plugin_object_id declaration for more info.
298 found
->second
.plugin_object_id
= 0;
302 // This will optionally delete the info from live_vars_.
303 VarTracker::ObjectGettingZeroRef(iter
);
306 bool PluginVarTracker::DeleteObjectInfoIfNecessary(VarMap::iterator iter
) {
307 // Get the info before calling the base class's version of this function,
308 // which may delete the object.
309 ProxyObjectVar
* object
= iter
->second
.var
->AsProxyObjectVar();
310 HostVar
host_var(object
->dispatcher(), object
->host_var_id());
312 if (!VarTracker::DeleteObjectInfoIfNecessary(iter
))
315 // Clean up the host var mapping.
316 DCHECK(host_var_to_plugin_var_
.find(host_var
) !=
317 host_var_to_plugin_var_
.end());
318 host_var_to_plugin_var_
.erase(host_var
);
322 PP_Var
PluginVarTracker::GetOrCreateObjectVarID(ProxyObjectVar
* object
) {
323 // We can't use object->GetPPVar() because we don't want to affect the
324 // refcount, so we have to add everything manually here.
325 int32 var_id
= object
->GetExistingVarID();
327 var_id
= AddVarInternal(object
, ADD_VAR_CREATE_WITH_NO_REFERENCE
);
328 object
->AssignVarID(var_id
);
331 PP_Var ret
= { PP_VARTYPE_OBJECT
};
332 ret
.value
.as_id
= var_id
;
336 void PluginVarTracker::SendAddRefObjectMsg(
337 const ProxyObjectVar
& proxy_object
) {
339 proxy_object
.dispatcher()->Send(new PpapiHostMsg_PPBVar_AddRefObject(
340 API_ID_PPB_VAR_DEPRECATED
, proxy_object
.host_var_id(), &unused
));
343 void PluginVarTracker::SendReleaseObjectMsg(
344 const ProxyObjectVar
& proxy_object
) {
345 proxy_object
.dispatcher()->Send(new PpapiHostMsg_PPBVar_ReleaseObject(
346 API_ID_PPB_VAR_DEPRECATED
, proxy_object
.host_var_id()));
349 scoped_refptr
<ProxyObjectVar
> PluginVarTracker::FindOrMakePluginVarFromHostVar(
351 PluginDispatcher
* dispatcher
) {
352 DCHECK(var
.type
== PP_VARTYPE_OBJECT
);
353 HostVar
host_var(dispatcher
, var
.value
.as_id
);
355 HostVarToPluginVarMap::iterator found
=
356 host_var_to_plugin_var_
.find(host_var
);
357 if (found
== host_var_to_plugin_var_
.end()) {
358 // Create a new object.
359 return scoped_refptr
<ProxyObjectVar
>(
360 new ProxyObjectVar(dispatcher
, static_cast<int32
>(var
.value
.as_id
)));
363 // Have this host var, look up the object.
364 VarMap::iterator ret
= live_vars_
.find(found
->second
);
365 DCHECK(ret
!= live_vars_
.end());
367 // All objects should be proxy objects.
368 DCHECK(ret
->second
.var
->AsProxyObjectVar());
369 return scoped_refptr
<ProxyObjectVar
>(ret
->second
.var
->AsProxyObjectVar());