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/media_stream_audio_track_resource.h"
14 #include "ppapi/proxy/media_stream_video_track_resource.h"
15 #include "ppapi/proxy/plugin_array_buffer_var.h"
16 #include "ppapi/proxy/plugin_dispatcher.h"
17 #include "ppapi/proxy/plugin_globals.h"
18 #include "ppapi/proxy/plugin_resource_var.h"
19 #include "ppapi/proxy/ppapi_messages.h"
20 #include "ppapi/proxy/proxy_object_var.h"
21 #include "ppapi/shared_impl/api_id.h"
22 #include "ppapi/shared_impl/ppapi_globals.h"
23 #include "ppapi/shared_impl/proxy_lock.h"
24 #include "ppapi/shared_impl/resource_tracker.h"
25 #include "ppapi/shared_impl/var.h"
32 Connection
GetConnectionForInstance(PP_Instance instance
) {
33 PluginDispatcher
* dispatcher
= PluginDispatcher::GetForInstance(instance
);
35 return Connection(PluginGlobals::Get()->GetBrowserSender(), dispatcher
);
40 PluginVarTracker::HostVar::HostVar(PluginDispatcher
* d
, int32 i
)
45 bool PluginVarTracker::HostVar::operator<(const HostVar
& other
) const {
46 if (dispatcher
< other
.dispatcher
)
48 if (other
.dispatcher
< dispatcher
)
50 return host_object_id
< other
.host_object_id
;
53 PluginVarTracker::PluginVarTracker() : VarTracker(THREAD_SAFE
) {
56 PluginVarTracker::~PluginVarTracker() {
59 PP_Var
PluginVarTracker::ReceiveObjectPassRef(const PP_Var
& host_var
,
60 PluginDispatcher
* dispatcher
) {
61 CheckThreadingPreconditions();
62 DCHECK(host_var
.type
== PP_VARTYPE_OBJECT
);
65 scoped_refptr
<ProxyObjectVar
> object(
66 FindOrMakePluginVarFromHostVar(host_var
, dispatcher
));
68 // Actually create the PP_Var, this will add all the tracking info but not
69 // adjust any refcounts.
70 PP_Var ret
= GetOrCreateObjectVarID(object
.get());
72 VarInfo
& info
= GetLiveVar(ret
)->second
;
73 if (info
.ref_count
> 0) {
74 // We already had a reference to it before. That means the renderer now has
75 // two references on our behalf. We want to transfer that extra reference
76 // to our list. This means we addref in the plugin, and release the extra
77 // one in the renderer.
78 SendReleaseObjectMsg(*object
.get());
84 PP_Var
PluginVarTracker::TrackObjectWithNoReference(
85 const PP_Var
& host_var
,
86 PluginDispatcher
* dispatcher
) {
87 CheckThreadingPreconditions();
88 DCHECK(host_var
.type
== PP_VARTYPE_OBJECT
);
91 scoped_refptr
<ProxyObjectVar
> object(
92 FindOrMakePluginVarFromHostVar(host_var
, dispatcher
));
94 // Actually create the PP_Var, this will add all the tracking info but not
95 // adjust any refcounts.
96 PP_Var ret
= GetOrCreateObjectVarID(object
.get());
98 VarInfo
& info
= GetLiveVar(ret
)->second
;
99 info
.track_with_no_reference_count
++;
103 void PluginVarTracker::StopTrackingObjectWithNoReference(
104 const PP_Var
& plugin_var
) {
105 CheckThreadingPreconditions();
106 DCHECK(plugin_var
.type
== PP_VARTYPE_OBJECT
);
108 VarMap::iterator found
= GetLiveVar(plugin_var
);
109 if (found
== live_vars_
.end()) {
114 DCHECK(found
->second
.track_with_no_reference_count
> 0);
115 found
->second
.track_with_no_reference_count
--;
116 DeleteObjectInfoIfNecessary(found
);
119 PP_Var
PluginVarTracker::GetHostObject(const PP_Var
& plugin_object
) const {
120 CheckThreadingPreconditions();
121 if (plugin_object
.type
!= PP_VARTYPE_OBJECT
) {
123 return PP_MakeUndefined();
126 Var
* var
= GetVar(plugin_object
);
127 ProxyObjectVar
* object
= var
->AsProxyObjectVar();
130 return PP_MakeUndefined();
133 // Make a var with the host ID.
134 PP_Var ret
= { PP_VARTYPE_OBJECT
};
135 ret
.value
.as_id
= object
->host_var_id();
139 PluginDispatcher
* PluginVarTracker::DispatcherForPluginObject(
140 const PP_Var
& plugin_object
) const {
141 CheckThreadingPreconditions();
142 if (plugin_object
.type
!= PP_VARTYPE_OBJECT
)
145 VarMap::const_iterator found
= GetLiveVar(plugin_object
);
146 if (found
== live_vars_
.end())
149 ProxyObjectVar
* object
= found
->second
.var
->AsProxyObjectVar();
152 return object
->dispatcher();
155 void PluginVarTracker::ReleaseHostObject(PluginDispatcher
* dispatcher
,
156 const PP_Var
& host_object
) {
157 CheckThreadingPreconditions();
158 DCHECK(host_object
.type
== PP_VARTYPE_OBJECT
);
160 // Convert the host object to a normal var valid in the plugin.
161 HostVarToPluginVarMap::iterator found
= host_var_to_plugin_var_
.find(
162 HostVar(dispatcher
, static_cast<int32
>(host_object
.value
.as_id
)));
163 if (found
== host_var_to_plugin_var_
.end()) {
168 // Now just release the object given the plugin var ID.
169 ReleaseVar(found
->second
);
172 PP_Var
PluginVarTracker::MakeResourcePPVarFromMessage(
173 PP_Instance instance
,
174 const IPC::Message
& creation_message
,
175 int pending_renderer_id
,
176 int pending_browser_id
) {
177 switch (creation_message
.type()) {
178 case PpapiPluginMsg_FileSystem_CreateFromPendingHost::ID
: {
179 DCHECK(pending_renderer_id
);
180 DCHECK(pending_browser_id
);
181 PP_FileSystemType file_system_type
;
182 if (!UnpackMessage
<PpapiPluginMsg_FileSystem_CreateFromPendingHost
>(
183 creation_message
, &file_system_type
)) {
184 NOTREACHED() << "Invalid message of type "
185 "PpapiPluginMsg_FileSystem_CreateFromPendingHost";
186 return PP_MakeNull();
188 // Create a plugin-side resource and attach it to the host resource.
189 // Note: This only makes sense when the plugin is out of process (which
190 // should always be true when passing resource vars).
191 PP_Resource pp_resource
=
192 (new FileSystemResource(GetConnectionForInstance(instance
),
196 file_system_type
))->GetReference();
197 return MakeResourcePPVar(pp_resource
);
199 case PpapiPluginMsg_MediaStreamAudioTrack_CreateFromPendingHost::ID
: {
200 DCHECK(pending_renderer_id
);
201 std::string track_id
;
203 PpapiPluginMsg_MediaStreamAudioTrack_CreateFromPendingHost
>(
204 creation_message
, &track_id
)) {
206 "Invalid message of type "
207 "PpapiPluginMsg_MediaStreamAudioTrack_CreateFromPendingHost";
208 return PP_MakeNull();
210 PP_Resource pp_resource
=
211 (new MediaStreamAudioTrackResource(GetConnectionForInstance(instance
),
214 track_id
))->GetReference();
215 return MakeResourcePPVar(pp_resource
);
217 case PpapiPluginMsg_MediaStreamVideoTrack_CreateFromPendingHost::ID
: {
218 DCHECK(pending_renderer_id
);
219 std::string track_id
;
221 PpapiPluginMsg_MediaStreamVideoTrack_CreateFromPendingHost
>(
222 creation_message
, &track_id
)) {
224 "Invalid message of type "
225 "PpapiPluginMsg_MediaStreamVideoTrack_CreateFromPendingHost";
226 return PP_MakeNull();
228 PP_Resource pp_resource
=
229 (new MediaStreamVideoTrackResource(GetConnectionForInstance(instance
),
232 track_id
))->GetReference();
233 return MakeResourcePPVar(pp_resource
);
236 NOTREACHED() << "Creation message has unexpected type "
237 << creation_message
.type();
238 return PP_MakeNull();
243 ResourceVar
* PluginVarTracker::MakeResourceVar(PP_Resource pp_resource
) {
244 // The resource 0 returns a null resource var.
246 return new PluginResourceVar();
248 ResourceTracker
* resource_tracker
= PpapiGlobals::Get()->GetResourceTracker();
249 ppapi::Resource
* resource
= resource_tracker
->GetResource(pp_resource
);
250 // A non-existant resource other than 0 returns NULL.
253 return new PluginResourceVar(resource
);
256 void PluginVarTracker::DidDeleteInstance(PP_Instance instance
) {
257 // Calling the destructors on plugin objects may in turn release other
258 // objects which will mutate the map out from under us. So do a two-step
259 // process of identifying the ones to delete, and then delete them.
261 // See the comment above user_data_to_plugin_ in the header file. We assume
262 // there aren't that many objects so a brute-force search is reasonable.
263 std::vector
<void*> user_data_to_delete
;
264 for (UserDataToPluginImplementedVarMap::const_iterator i
=
265 user_data_to_plugin_
.begin();
266 i
!= user_data_to_plugin_
.end();
268 if (i
->second
.instance
== instance
)
269 user_data_to_delete
.push_back(i
->first
);
272 for (size_t i
= 0; i
< user_data_to_delete
.size(); i
++) {
273 UserDataToPluginImplementedVarMap::iterator found
=
274 user_data_to_plugin_
.find(user_data_to_delete
[i
]);
275 if (found
== user_data_to_plugin_
.end())
276 continue; // Object removed from list while we were iterating.
278 if (!found
->second
.plugin_object_id
) {
279 // This object is for the freed instance and the plugin is not holding
280 // any references to it. Deallocate immediately.
281 CallWhileUnlocked(found
->second
.ppp_class
->Deallocate
, found
->first
);
282 user_data_to_plugin_
.erase(found
);
284 // The plugin is holding refs to this object. We don't want to call
285 // Deallocate since the plugin may be depending on those refs to keep
286 // its data alive. To avoid crashes in this case, just clear out the
287 // instance to mark it and continue. When the plugin refs go to 0,
288 // we'll notice there is no instance and call Deallocate.
289 found
->second
.instance
= 0;
294 void PluginVarTracker::DidDeleteDispatcher(PluginDispatcher
* dispatcher
) {
295 for (VarMap::iterator it
= live_vars_
.begin();
296 it
!= live_vars_
.end();
298 if (it
->second
.var
.get() == NULL
)
300 ProxyObjectVar
* object
= it
->second
.var
->AsProxyObjectVar();
301 if (object
&& object
->dispatcher() == dispatcher
)
302 object
->clear_dispatcher();
306 ArrayBufferVar
* PluginVarTracker::CreateArrayBuffer(uint32 size_in_bytes
) {
307 return new PluginArrayBufferVar(size_in_bytes
);
310 ArrayBufferVar
* PluginVarTracker::CreateShmArrayBuffer(
311 uint32 size_in_bytes
,
312 base::SharedMemoryHandle handle
) {
313 return new PluginArrayBufferVar(size_in_bytes
, handle
);
316 void PluginVarTracker::PluginImplementedObjectCreated(
317 PP_Instance instance
,
318 const PP_Var
& created_var
,
319 const PPP_Class_Deprecated
* ppp_class
,
320 void* ppp_class_data
) {
321 PluginImplementedVar p
;
322 p
.ppp_class
= ppp_class
;
323 p
.instance
= instance
;
324 p
.plugin_object_id
= created_var
.value
.as_id
;
325 user_data_to_plugin_
[ppp_class_data
] = p
;
327 // Link the user data to the object.
328 ProxyObjectVar
* object
= GetVar(created_var
)->AsProxyObjectVar();
329 object
->set_user_data(ppp_class_data
);
332 void PluginVarTracker::PluginImplementedObjectDestroyed(void* user_data
) {
333 UserDataToPluginImplementedVarMap::iterator found
=
334 user_data_to_plugin_
.find(user_data
);
335 if (found
== user_data_to_plugin_
.end()) {
339 user_data_to_plugin_
.erase(found
);
342 bool PluginVarTracker::IsPluginImplementedObjectAlive(void* user_data
) {
343 return user_data_to_plugin_
.find(user_data
) != user_data_to_plugin_
.end();
346 bool PluginVarTracker::ValidatePluginObjectCall(
347 const PPP_Class_Deprecated
* ppp_class
,
349 UserDataToPluginImplementedVarMap::iterator found
=
350 user_data_to_plugin_
.find(user_data
);
351 if (found
== user_data_to_plugin_
.end())
353 return found
->second
.ppp_class
== ppp_class
;
356 int32
PluginVarTracker::AddVarInternal(Var
* var
, AddVarRefMode mode
) {
358 int32 new_id
= VarTracker::AddVarInternal(var
, mode
);
360 // Need to add proxy objects to the host var map.
361 ProxyObjectVar
* proxy_object
= var
->AsProxyObjectVar();
363 HostVar
host_var(proxy_object
->dispatcher(), proxy_object
->host_var_id());
364 // TODO(teravest): Change to DCHECK when http://crbug.com/276347 is
366 CHECK(host_var_to_plugin_var_
.find(host_var
) ==
367 host_var_to_plugin_var_
.end()); // Adding an object twice, use
368 // FindOrMakePluginVarFromHostVar.
369 host_var_to_plugin_var_
[host_var
] = new_id
;
374 void PluginVarTracker::TrackedObjectGettingOneRef(VarMap::const_iterator iter
) {
375 ProxyObjectVar
* object
= iter
->second
.var
->AsProxyObjectVar();
381 DCHECK(iter
->second
.ref_count
== 0);
383 // Got an AddRef for an object we have no existing reference for.
384 // We need to tell the browser we've taken a ref. This comes up when the
385 // browser passes an object as an input param and holds a ref for us.
386 // This must be a sync message since otherwise the "addref" will actually
387 // occur after the return to the browser of the sync function that
388 // presumably sent the object.
389 SendAddRefObjectMsg(*object
);
392 void PluginVarTracker::ObjectGettingZeroRef(VarMap::iterator iter
) {
393 ProxyObjectVar
* object
= iter
->second
.var
->AsProxyObjectVar();
399 // Notify the host we're no longer holding our ref.
400 DCHECK(iter
->second
.ref_count
== 0);
401 SendReleaseObjectMsg(*object
);
403 UserDataToPluginImplementedVarMap::iterator found
=
404 user_data_to_plugin_
.find(object
->user_data());
405 if (found
!= user_data_to_plugin_
.end()) {
406 // This object is implemented in the plugin.
407 if (found
->second
.instance
== 0) {
408 // Instance is destroyed. This means that we'll never get a Deallocate
409 // call from the renderer and we should do so now.
410 found
->second
.ppp_class
->Deallocate(found
->first
);
411 user_data_to_plugin_
.erase(found
);
413 // The plugin is releasing its last reference to an object it implements.
414 // Clear the tracking data that links our "plugin implemented object" to
415 // the var. If the instance is destroyed and there is no ID, we know that
416 // we should just call Deallocate on the object data.
418 // See the plugin_object_id declaration for more info.
419 found
->second
.plugin_object_id
= 0;
423 // This will optionally delete the info from live_vars_.
424 VarTracker::ObjectGettingZeroRef(iter
);
427 bool PluginVarTracker::DeleteObjectInfoIfNecessary(VarMap::iterator iter
) {
428 // Get the info before calling the base class's version of this function,
429 // which may delete the object.
430 ProxyObjectVar
* object
= iter
->second
.var
->AsProxyObjectVar();
431 HostVar
host_var(object
->dispatcher(), object
->host_var_id());
433 if (!VarTracker::DeleteObjectInfoIfNecessary(iter
))
436 // Clean up the host var mapping.
437 DCHECK(host_var_to_plugin_var_
.find(host_var
) !=
438 host_var_to_plugin_var_
.end());
439 host_var_to_plugin_var_
.erase(host_var
);
443 PP_Var
PluginVarTracker::GetOrCreateObjectVarID(ProxyObjectVar
* object
) {
444 // We can't use object->GetPPVar() because we don't want to affect the
445 // refcount, so we have to add everything manually here.
446 int32 var_id
= object
->GetExistingVarID();
448 var_id
= AddVarInternal(object
, ADD_VAR_CREATE_WITH_NO_REFERENCE
);
449 object
->AssignVarID(var_id
);
452 PP_Var ret
= { PP_VARTYPE_OBJECT
};
453 ret
.value
.as_id
= var_id
;
457 void PluginVarTracker::SendAddRefObjectMsg(
458 const ProxyObjectVar
& proxy_object
) {
459 if (proxy_object
.dispatcher()) {
460 proxy_object
.dispatcher()->Send(new PpapiHostMsg_PPBVar_AddRefObject(
461 API_ID_PPB_VAR_DEPRECATED
, proxy_object
.host_var_id()));
465 void PluginVarTracker::SendReleaseObjectMsg(
466 const ProxyObjectVar
& proxy_object
) {
467 if (proxy_object
.dispatcher()) {
468 proxy_object
.dispatcher()->Send(new PpapiHostMsg_PPBVar_ReleaseObject(
469 API_ID_PPB_VAR_DEPRECATED
, proxy_object
.host_var_id()));
473 scoped_refptr
<ProxyObjectVar
> PluginVarTracker::FindOrMakePluginVarFromHostVar(
475 PluginDispatcher
* dispatcher
) {
476 DCHECK(var
.type
== PP_VARTYPE_OBJECT
);
477 HostVar
host_var(dispatcher
, var
.value
.as_id
);
479 HostVarToPluginVarMap::iterator found
=
480 host_var_to_plugin_var_
.find(host_var
);
481 if (found
== host_var_to_plugin_var_
.end()) {
482 // Create a new object.
483 return scoped_refptr
<ProxyObjectVar
>(
484 new ProxyObjectVar(dispatcher
, static_cast<int32
>(var
.value
.as_id
)));
487 // Have this host var, look up the object.
488 VarMap::iterator ret
= live_vars_
.find(found
->second
);
490 // We CHECK here because we currently don't fall back sanely.
491 // This may be involved in a NULL dereference. http://crbug.com/276347
492 CHECK(ret
!= live_vars_
.end());
494 // All objects should be proxy objects.
495 DCHECK(ret
->second
.var
->AsProxyObjectVar());
496 return scoped_refptr
<ProxyObjectVar
>(ret
->second
.var
->AsProxyObjectVar());
499 int PluginVarTracker::TrackSharedMemoryHandle(PP_Instance instance
,
500 base::SharedMemoryHandle handle
,
501 uint32 size_in_bytes
) {
506 bool PluginVarTracker::StopTrackingSharedMemoryHandle(
508 PP_Instance instance
,
509 base::SharedMemoryHandle
* handle
,
510 uint32
* size_in_bytes
) {