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/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"
26
27 namespace ppapi {
28 namespace proxy {
29
30 namespace {
31
GetConnectionForInstance(PP_Instance instance)32 Connection GetConnectionForInstance(PP_Instance instance) {
33 PluginDispatcher* dispatcher = PluginDispatcher::GetForInstance(instance);
34 DCHECK(dispatcher);
35 return Connection(PluginGlobals::Get()->GetBrowserSender(), dispatcher);
36 }
37
38 } // namespace
39
HostVar(PluginDispatcher * d,int32 i)40 PluginVarTracker::HostVar::HostVar(PluginDispatcher* d, int32 i)
41 : dispatcher(d),
42 host_object_id(i) {
43 }
44
operator <(const HostVar & other) const45 bool PluginVarTracker::HostVar::operator<(const HostVar& other) const {
46 if (dispatcher < other.dispatcher)
47 return true;
48 if (other.dispatcher < dispatcher)
49 return false;
50 return host_object_id < other.host_object_id;
51 }
52
PluginVarTracker()53 PluginVarTracker::PluginVarTracker() : VarTracker(THREAD_SAFE) {
54 }
55
~PluginVarTracker()56 PluginVarTracker::~PluginVarTracker() {
57 }
58
ReceiveObjectPassRef(const PP_Var & host_var,PluginDispatcher * dispatcher)59 PP_Var PluginVarTracker::ReceiveObjectPassRef(const PP_Var& host_var,
60 PluginDispatcher* dispatcher) {
61 CheckThreadingPreconditions();
62 DCHECK(host_var.type == PP_VARTYPE_OBJECT);
63
64 // Get the object.
65 scoped_refptr<ProxyObjectVar> object(
66 FindOrMakePluginVarFromHostVar(host_var, dispatcher));
67
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());
71
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());
79 }
80 info.ref_count++;
81 return ret;
82 }
83
TrackObjectWithNoReference(const PP_Var & host_var,PluginDispatcher * dispatcher)84 PP_Var PluginVarTracker::TrackObjectWithNoReference(
85 const PP_Var& host_var,
86 PluginDispatcher* dispatcher) {
87 CheckThreadingPreconditions();
88 DCHECK(host_var.type == PP_VARTYPE_OBJECT);
89
90 // Get the object.
91 scoped_refptr<ProxyObjectVar> object(
92 FindOrMakePluginVarFromHostVar(host_var, dispatcher));
93
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());
97
98 VarInfo& info = GetLiveVar(ret)->second;
99 info.track_with_no_reference_count++;
100 return ret;
101 }
102
StopTrackingObjectWithNoReference(const PP_Var & plugin_var)103 void PluginVarTracker::StopTrackingObjectWithNoReference(
104 const PP_Var& plugin_var) {
105 CheckThreadingPreconditions();
106 DCHECK(plugin_var.type == PP_VARTYPE_OBJECT);
107
108 VarMap::iterator found = GetLiveVar(plugin_var);
109 if (found == live_vars_.end()) {
110 NOTREACHED();
111 return;
112 }
113
114 DCHECK(found->second.track_with_no_reference_count > 0);
115 found->second.track_with_no_reference_count--;
116 DeleteObjectInfoIfNecessary(found);
117 }
118
GetHostObject(const PP_Var & plugin_object) const119 PP_Var PluginVarTracker::GetHostObject(const PP_Var& plugin_object) const {
120 CheckThreadingPreconditions();
121 if (plugin_object.type != PP_VARTYPE_OBJECT) {
122 NOTREACHED();
123 return PP_MakeUndefined();
124 }
125
126 Var* var = GetVar(plugin_object);
127 ProxyObjectVar* object = var->AsProxyObjectVar();
128 if (!object) {
129 NOTREACHED();
130 return PP_MakeUndefined();
131 }
132
133 // Make a var with the host ID.
134 PP_Var ret = { PP_VARTYPE_OBJECT };
135 ret.value.as_id = object->host_var_id();
136 return ret;
137 }
138
DispatcherForPluginObject(const PP_Var & plugin_object) const139 PluginDispatcher* PluginVarTracker::DispatcherForPluginObject(
140 const PP_Var& plugin_object) const {
141 CheckThreadingPreconditions();
142 if (plugin_object.type != PP_VARTYPE_OBJECT)
143 return NULL;
144
145 VarMap::const_iterator found = GetLiveVar(plugin_object);
146 if (found == live_vars_.end())
147 return NULL;
148
149 ProxyObjectVar* object = found->second.var->AsProxyObjectVar();
150 if (!object)
151 return NULL;
152 return object->dispatcher();
153 }
154
ReleaseHostObject(PluginDispatcher * dispatcher,const PP_Var & host_object)155 void PluginVarTracker::ReleaseHostObject(PluginDispatcher* dispatcher,
156 const PP_Var& host_object) {
157 CheckThreadingPreconditions();
158 DCHECK(host_object.type == PP_VARTYPE_OBJECT);
159
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()) {
164 NOTREACHED();
165 return;
166 }
167
168 // Now just release the object given the plugin var ID.
169 ReleaseVar(found->second);
170 }
171
MakeResourcePPVarFromMessage(PP_Instance instance,const IPC::Message & creation_message,int pending_renderer_id,int pending_browser_id)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();
187 }
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),
193 instance,
194 pending_renderer_id,
195 pending_browser_id,
196 file_system_type))->GetReference();
197 return MakeResourcePPVar(pp_resource);
198 }
199 case PpapiPluginMsg_MediaStreamAudioTrack_CreateFromPendingHost::ID: {
200 DCHECK(pending_renderer_id);
201 std::string track_id;
202 if (!UnpackMessage<
203 PpapiPluginMsg_MediaStreamAudioTrack_CreateFromPendingHost>(
204 creation_message, &track_id)) {
205 NOTREACHED() <<
206 "Invalid message of type "
207 "PpapiPluginMsg_MediaStreamAudioTrack_CreateFromPendingHost";
208 return PP_MakeNull();
209 }
210 PP_Resource pp_resource =
211 (new MediaStreamAudioTrackResource(GetConnectionForInstance(instance),
212 instance,
213 pending_renderer_id,
214 track_id))->GetReference();
215 return MakeResourcePPVar(pp_resource);
216 }
217 case PpapiPluginMsg_MediaStreamVideoTrack_CreateFromPendingHost::ID: {
218 DCHECK(pending_renderer_id);
219 std::string track_id;
220 if (!UnpackMessage<
221 PpapiPluginMsg_MediaStreamVideoTrack_CreateFromPendingHost>(
222 creation_message, &track_id)) {
223 NOTREACHED() <<
224 "Invalid message of type "
225 "PpapiPluginMsg_MediaStreamVideoTrack_CreateFromPendingHost";
226 return PP_MakeNull();
227 }
228 PP_Resource pp_resource =
229 (new MediaStreamVideoTrackResource(GetConnectionForInstance(instance),
230 instance,
231 pending_renderer_id,
232 track_id))->GetReference();
233 return MakeResourcePPVar(pp_resource);
234 }
235 default: {
236 NOTREACHED() << "Creation message has unexpected type "
237 << creation_message.type();
238 return PP_MakeNull();
239 }
240 }
241 }
242
MakeResourceVar(PP_Resource pp_resource)243 ResourceVar* PluginVarTracker::MakeResourceVar(PP_Resource pp_resource) {
244 // The resource 0 returns a null resource var.
245 if (!pp_resource)
246 return new PluginResourceVar();
247
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.
251 if (!resource)
252 return NULL;
253 return new PluginResourceVar(resource);
254 }
255
DidDeleteInstance(PP_Instance instance)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.
260 //
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();
267 ++i) {
268 if (i->second.instance == instance)
269 user_data_to_delete.push_back(i->first);
270 }
271
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.
277
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);
283 } else {
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;
290 }
291 }
292 }
293
DidDeleteDispatcher(PluginDispatcher * dispatcher)294 void PluginVarTracker::DidDeleteDispatcher(PluginDispatcher* dispatcher) {
295 for (VarMap::iterator it = live_vars_.begin();
296 it != live_vars_.end();
297 ++it) {
298 if (it->second.var.get() == NULL)
299 continue;
300 ProxyObjectVar* object = it->second.var->AsProxyObjectVar();
301 if (object && object->dispatcher() == dispatcher)
302 object->clear_dispatcher();
303 }
304 }
305
CreateArrayBuffer(uint32 size_in_bytes)306 ArrayBufferVar* PluginVarTracker::CreateArrayBuffer(uint32 size_in_bytes) {
307 return new PluginArrayBufferVar(size_in_bytes);
308 }
309
CreateShmArrayBuffer(uint32 size_in_bytes,base::SharedMemoryHandle handle)310 ArrayBufferVar* PluginVarTracker::CreateShmArrayBuffer(
311 uint32 size_in_bytes,
312 base::SharedMemoryHandle handle) {
313 return new PluginArrayBufferVar(size_in_bytes, handle);
314 }
315
PluginImplementedObjectCreated(PP_Instance instance,const PP_Var & created_var,const PPP_Class_Deprecated * ppp_class,void * ppp_class_data)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;
326
327 // Link the user data to the object.
328 ProxyObjectVar* object = GetVar(created_var)->AsProxyObjectVar();
329 object->set_user_data(ppp_class_data);
330 }
331
PluginImplementedObjectDestroyed(void * user_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()) {
336 NOTREACHED();
337 return;
338 }
339 user_data_to_plugin_.erase(found);
340 }
341
IsPluginImplementedObjectAlive(void * user_data)342 bool PluginVarTracker::IsPluginImplementedObjectAlive(void* user_data) {
343 return user_data_to_plugin_.find(user_data) != user_data_to_plugin_.end();
344 }
345
ValidatePluginObjectCall(const PPP_Class_Deprecated * ppp_class,void * user_data)346 bool PluginVarTracker::ValidatePluginObjectCall(
347 const PPP_Class_Deprecated* ppp_class,
348 void* user_data) {
349 UserDataToPluginImplementedVarMap::iterator found =
350 user_data_to_plugin_.find(user_data);
351 if (found == user_data_to_plugin_.end())
352 return false;
353 return found->second.ppp_class == ppp_class;
354 }
355
AddVarInternal(Var * var,AddVarRefMode mode)356 int32 PluginVarTracker::AddVarInternal(Var* var, AddVarRefMode mode) {
357 // Normal adding.
358 int32 new_id = VarTracker::AddVarInternal(var, mode);
359
360 // Need to add proxy objects to the host var map.
361 ProxyObjectVar* proxy_object = var->AsProxyObjectVar();
362 if (proxy_object) {
363 HostVar host_var(proxy_object->dispatcher(), proxy_object->host_var_id());
364 // TODO(teravest): Change to DCHECK when http://crbug.com/276347 is
365 // resolved.
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;
370 }
371 return new_id;
372 }
373
TrackedObjectGettingOneRef(VarMap::const_iterator iter)374 void PluginVarTracker::TrackedObjectGettingOneRef(VarMap::const_iterator iter) {
375 ProxyObjectVar* object = iter->second.var->AsProxyObjectVar();
376 if (!object) {
377 NOTREACHED();
378 return;
379 }
380
381 DCHECK(iter->second.ref_count == 0);
382
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);
390 }
391
ObjectGettingZeroRef(VarMap::iterator iter)392 void PluginVarTracker::ObjectGettingZeroRef(VarMap::iterator iter) {
393 ProxyObjectVar* object = iter->second.var->AsProxyObjectVar();
394 if (!object) {
395 NOTREACHED();
396 return;
397 }
398
399 // Notify the host we're no longer holding our ref.
400 DCHECK(iter->second.ref_count == 0);
401 SendReleaseObjectMsg(*object);
402
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);
412 } else {
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.
417 //
418 // See the plugin_object_id declaration for more info.
419 found->second.plugin_object_id = 0;
420 }
421 }
422
423 // This will optionally delete the info from live_vars_.
424 VarTracker::ObjectGettingZeroRef(iter);
425 }
426
DeleteObjectInfoIfNecessary(VarMap::iterator 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());
432
433 if (!VarTracker::DeleteObjectInfoIfNecessary(iter))
434 return false;
435
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);
440 return true;
441 }
442
GetOrCreateObjectVarID(ProxyObjectVar * object)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();
447 if (!var_id) {
448 var_id = AddVarInternal(object, ADD_VAR_CREATE_WITH_NO_REFERENCE);
449 object->AssignVarID(var_id);
450 }
451
452 PP_Var ret = { PP_VARTYPE_OBJECT };
453 ret.value.as_id = var_id;
454 return ret;
455 }
456
SendAddRefObjectMsg(const ProxyObjectVar & proxy_object)457 void PluginVarTracker::SendAddRefObjectMsg(
458 const ProxyObjectVar& proxy_object) {
459 int unused;
460 if (proxy_object.dispatcher()) {
461 proxy_object.dispatcher()->Send(new PpapiHostMsg_PPBVar_AddRefObject(
462 API_ID_PPB_VAR_DEPRECATED, proxy_object.host_var_id(), &unused));
463 }
464 }
465
SendReleaseObjectMsg(const ProxyObjectVar & proxy_object)466 void PluginVarTracker::SendReleaseObjectMsg(
467 const ProxyObjectVar& proxy_object) {
468 if (proxy_object.dispatcher()) {
469 proxy_object.dispatcher()->Send(new PpapiHostMsg_PPBVar_ReleaseObject(
470 API_ID_PPB_VAR_DEPRECATED, proxy_object.host_var_id()));
471 }
472 }
473
FindOrMakePluginVarFromHostVar(const PP_Var & var,PluginDispatcher * dispatcher)474 scoped_refptr<ProxyObjectVar> PluginVarTracker::FindOrMakePluginVarFromHostVar(
475 const PP_Var& var,
476 PluginDispatcher* dispatcher) {
477 DCHECK(var.type == PP_VARTYPE_OBJECT);
478 HostVar host_var(dispatcher, var.value.as_id);
479
480 HostVarToPluginVarMap::iterator found =
481 host_var_to_plugin_var_.find(host_var);
482 if (found == host_var_to_plugin_var_.end()) {
483 // Create a new object.
484 return scoped_refptr<ProxyObjectVar>(
485 new ProxyObjectVar(dispatcher, static_cast<int32>(var.value.as_id)));
486 }
487
488 // Have this host var, look up the object.
489 VarMap::iterator ret = live_vars_.find(found->second);
490
491 // We CHECK here because we currently don't fall back sanely.
492 // This may be involved in a NULL dereference. http://crbug.com/276347
493 CHECK(ret != live_vars_.end());
494
495 // All objects should be proxy objects.
496 DCHECK(ret->second.var->AsProxyObjectVar());
497 return scoped_refptr<ProxyObjectVar>(ret->second.var->AsProxyObjectVar());
498 }
499
TrackSharedMemoryHandle(PP_Instance instance,base::SharedMemoryHandle handle,uint32 size_in_bytes)500 int PluginVarTracker::TrackSharedMemoryHandle(PP_Instance instance,
501 base::SharedMemoryHandle handle,
502 uint32 size_in_bytes) {
503 NOTREACHED();
504 return -1;
505 }
506
StopTrackingSharedMemoryHandle(int id,PP_Instance instance,base::SharedMemoryHandle * handle,uint32 * size_in_bytes)507 bool PluginVarTracker::StopTrackingSharedMemoryHandle(
508 int id,
509 PP_Instance instance,
510 base::SharedMemoryHandle* handle,
511 uint32* size_in_bytes) {
512 NOTREACHED();
513 return false;
514 }
515
516 } // namesace proxy
517 } // namespace ppapi
518