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/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"
24
25 namespace ppapi {
26 namespace proxy {
27
28 namespace {
29
GetConnectionForInstance(PP_Instance instance)30 Connection GetConnectionForInstance(PP_Instance instance) {
31 PluginDispatcher* dispatcher = PluginDispatcher::GetForInstance(instance);
32 DCHECK(dispatcher);
33 return Connection(PluginGlobals::Get()->GetBrowserSender(), dispatcher);
34 }
35
36 } // namespace
37
HostVar(PluginDispatcher * d,int32 i)38 PluginVarTracker::HostVar::HostVar(PluginDispatcher* d, int32 i)
39 : dispatcher(d),
40 host_object_id(i) {
41 }
42
operator <(const HostVar & other) const43 bool PluginVarTracker::HostVar::operator<(const HostVar& other) const {
44 if (dispatcher < other.dispatcher)
45 return true;
46 if (other.dispatcher < dispatcher)
47 return false;
48 return host_object_id < other.host_object_id;
49 }
50
PluginVarTracker()51 PluginVarTracker::PluginVarTracker() : VarTracker(THREAD_SAFE) {
52 }
53
~PluginVarTracker()54 PluginVarTracker::~PluginVarTracker() {
55 }
56
ReceiveObjectPassRef(const PP_Var & host_var,PluginDispatcher * dispatcher)57 PP_Var PluginVarTracker::ReceiveObjectPassRef(const PP_Var& host_var,
58 PluginDispatcher* dispatcher) {
59 CheckThreadingPreconditions();
60 DCHECK(host_var.type == PP_VARTYPE_OBJECT);
61
62 // Get the object.
63 scoped_refptr<ProxyObjectVar> object(
64 FindOrMakePluginVarFromHostVar(host_var, dispatcher));
65
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());
69
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());
77 }
78 info.ref_count++;
79 return ret;
80 }
81
TrackObjectWithNoReference(const PP_Var & host_var,PluginDispatcher * dispatcher)82 PP_Var PluginVarTracker::TrackObjectWithNoReference(
83 const PP_Var& host_var,
84 PluginDispatcher* dispatcher) {
85 CheckThreadingPreconditions();
86 DCHECK(host_var.type == PP_VARTYPE_OBJECT);
87
88 // Get the object.
89 scoped_refptr<ProxyObjectVar> object(
90 FindOrMakePluginVarFromHostVar(host_var, dispatcher));
91
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());
95
96 VarInfo& info = GetLiveVar(ret)->second;
97 info.track_with_no_reference_count++;
98 return ret;
99 }
100
StopTrackingObjectWithNoReference(const PP_Var & plugin_var)101 void PluginVarTracker::StopTrackingObjectWithNoReference(
102 const PP_Var& plugin_var) {
103 CheckThreadingPreconditions();
104 DCHECK(plugin_var.type == PP_VARTYPE_OBJECT);
105
106 VarMap::iterator found = GetLiveVar(plugin_var);
107 if (found == live_vars_.end()) {
108 NOTREACHED();
109 return;
110 }
111
112 DCHECK(found->second.track_with_no_reference_count > 0);
113 found->second.track_with_no_reference_count--;
114 DeleteObjectInfoIfNecessary(found);
115 }
116
GetHostObject(const PP_Var & plugin_object) const117 PP_Var PluginVarTracker::GetHostObject(const PP_Var& plugin_object) const {
118 CheckThreadingPreconditions();
119 if (plugin_object.type != PP_VARTYPE_OBJECT) {
120 NOTREACHED();
121 return PP_MakeUndefined();
122 }
123
124 Var* var = GetVar(plugin_object);
125 ProxyObjectVar* object = var->AsProxyObjectVar();
126 if (!object) {
127 NOTREACHED();
128 return PP_MakeUndefined();
129 }
130
131 // Make a var with the host ID.
132 PP_Var ret = { PP_VARTYPE_OBJECT };
133 ret.value.as_id = object->host_var_id();
134 return ret;
135 }
136
DispatcherForPluginObject(const PP_Var & plugin_object) const137 PluginDispatcher* PluginVarTracker::DispatcherForPluginObject(
138 const PP_Var& plugin_object) const {
139 CheckThreadingPreconditions();
140 if (plugin_object.type != PP_VARTYPE_OBJECT)
141 return NULL;
142
143 VarMap::const_iterator found = GetLiveVar(plugin_object);
144 if (found == live_vars_.end())
145 return NULL;
146
147 ProxyObjectVar* object = found->second.var->AsProxyObjectVar();
148 if (!object)
149 return NULL;
150 return object->dispatcher();
151 }
152
ReleaseHostObject(PluginDispatcher * dispatcher,const PP_Var & host_object)153 void PluginVarTracker::ReleaseHostObject(PluginDispatcher* dispatcher,
154 const PP_Var& host_object) {
155 CheckThreadingPreconditions();
156 DCHECK(host_object.type == PP_VARTYPE_OBJECT);
157
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()) {
162 NOTREACHED();
163 return;
164 }
165
166 // Now just release the object given the plugin var ID.
167 ReleaseVar(found->second);
168 }
169
MakeResourcePPVarFromMessage(PP_Instance instance,const IPC::Message & creation_message,int pending_renderer_id,int pending_browser_id)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();
185 }
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),
191 instance,
192 pending_renderer_id,
193 pending_browser_id,
194 file_system_type))->GetReference();
195 return MakeResourcePPVar(pp_resource);
196 }
197 default: {
198 NOTREACHED() << "Creation message has unexpected type "
199 << creation_message.type();
200 return PP_MakeNull();
201 }
202 }
203 }
204
MakeResourceVar(PP_Resource pp_resource)205 ResourceVar* PluginVarTracker::MakeResourceVar(PP_Resource pp_resource) {
206 // The resource 0 returns a null resource var.
207 if (!pp_resource)
208 return new PluginResourceVar();
209
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.
213 if (!resource)
214 return NULL;
215 return new PluginResourceVar(resource);
216 }
217
DidDeleteInstance(PP_Instance instance)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.
222 //
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();
229 ++i) {
230 if (i->second.instance == instance)
231 user_data_to_delete.push_back(i->first);
232 }
233
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.
239
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);
245 } else {
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;
252 }
253 }
254 }
255
DidDeleteDispatcher(PluginDispatcher * dispatcher)256 void PluginVarTracker::DidDeleteDispatcher(PluginDispatcher* dispatcher) {
257 for (VarMap::iterator it = live_vars_.begin();
258 it != live_vars_.end();
259 ++it) {
260 if (it->second.var.get() == NULL)
261 continue;
262 ProxyObjectVar* object = it->second.var->AsProxyObjectVar();
263 if (object && object->dispatcher() == dispatcher)
264 object->clear_dispatcher();
265 }
266 }
267
CreateArrayBuffer(uint32 size_in_bytes)268 ArrayBufferVar* PluginVarTracker::CreateArrayBuffer(uint32 size_in_bytes) {
269 return new PluginArrayBufferVar(size_in_bytes);
270 }
271
CreateShmArrayBuffer(uint32 size_in_bytes,base::SharedMemoryHandle handle)272 ArrayBufferVar* PluginVarTracker::CreateShmArrayBuffer(
273 uint32 size_in_bytes,
274 base::SharedMemoryHandle handle) {
275 return new PluginArrayBufferVar(size_in_bytes, handle);
276 }
277
PluginImplementedObjectCreated(PP_Instance instance,const PP_Var & created_var,const PPP_Class_Deprecated * ppp_class,void * ppp_class_data)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;
288
289 // Link the user data to the object.
290 ProxyObjectVar* object = GetVar(created_var)->AsProxyObjectVar();
291 object->set_user_data(ppp_class_data);
292 }
293
PluginImplementedObjectDestroyed(void * user_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()) {
298 NOTREACHED();
299 return;
300 }
301 user_data_to_plugin_.erase(found);
302 }
303
IsPluginImplementedObjectAlive(void * user_data)304 bool PluginVarTracker::IsPluginImplementedObjectAlive(void* user_data) {
305 return user_data_to_plugin_.find(user_data) != user_data_to_plugin_.end();
306 }
307
ValidatePluginObjectCall(const PPP_Class_Deprecated * ppp_class,void * user_data)308 bool PluginVarTracker::ValidatePluginObjectCall(
309 const PPP_Class_Deprecated* ppp_class,
310 void* user_data) {
311 UserDataToPluginImplementedVarMap::iterator found =
312 user_data_to_plugin_.find(user_data);
313 if (found == user_data_to_plugin_.end())
314 return false;
315 return found->second.ppp_class == ppp_class;
316 }
317
AddVarInternal(Var * var,AddVarRefMode mode)318 int32 PluginVarTracker::AddVarInternal(Var* var, AddVarRefMode mode) {
319 // Normal adding.
320 int32 new_id = VarTracker::AddVarInternal(var, mode);
321
322 // Need to add proxy objects to the host var map.
323 ProxyObjectVar* proxy_object = var->AsProxyObjectVar();
324 if (proxy_object) {
325 HostVar host_var(proxy_object->dispatcher(), proxy_object->host_var_id());
326 // TODO(teravest): Change to DCHECK when http://crbug.com/276347 is
327 // resolved.
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;
332 }
333 return new_id;
334 }
335
TrackedObjectGettingOneRef(VarMap::const_iterator iter)336 void PluginVarTracker::TrackedObjectGettingOneRef(VarMap::const_iterator iter) {
337 ProxyObjectVar* object = iter->second.var->AsProxyObjectVar();
338 if (!object) {
339 NOTREACHED();
340 return;
341 }
342
343 DCHECK(iter->second.ref_count == 0);
344
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);
352 }
353
ObjectGettingZeroRef(VarMap::iterator iter)354 void PluginVarTracker::ObjectGettingZeroRef(VarMap::iterator iter) {
355 ProxyObjectVar* object = iter->second.var->AsProxyObjectVar();
356 if (!object) {
357 NOTREACHED();
358 return;
359 }
360
361 // Notify the host we're no longer holding our ref.
362 DCHECK(iter->second.ref_count == 0);
363 SendReleaseObjectMsg(*object);
364
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);
374 } else {
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.
379 //
380 // See the plugin_object_id declaration for more info.
381 found->second.plugin_object_id = 0;
382 }
383 }
384
385 // This will optionally delete the info from live_vars_.
386 VarTracker::ObjectGettingZeroRef(iter);
387 }
388
DeleteObjectInfoIfNecessary(VarMap::iterator 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());
394
395 if (!VarTracker::DeleteObjectInfoIfNecessary(iter))
396 return false;
397
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);
402 return true;
403 }
404
GetOrCreateObjectVarID(ProxyObjectVar * object)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();
409 if (!var_id) {
410 var_id = AddVarInternal(object, ADD_VAR_CREATE_WITH_NO_REFERENCE);
411 object->AssignVarID(var_id);
412 }
413
414 PP_Var ret = { PP_VARTYPE_OBJECT };
415 ret.value.as_id = var_id;
416 return ret;
417 }
418
SendAddRefObjectMsg(const ProxyObjectVar & proxy_object)419 void PluginVarTracker::SendAddRefObjectMsg(
420 const ProxyObjectVar& proxy_object) {
421 int unused;
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));
425 }
426 }
427
SendReleaseObjectMsg(const ProxyObjectVar & proxy_object)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()));
433 }
434 }
435
FindOrMakePluginVarFromHostVar(const PP_Var & var,PluginDispatcher * dispatcher)436 scoped_refptr<ProxyObjectVar> PluginVarTracker::FindOrMakePluginVarFromHostVar(
437 const PP_Var& var,
438 PluginDispatcher* dispatcher) {
439 DCHECK(var.type == PP_VARTYPE_OBJECT);
440 HostVar host_var(dispatcher, var.value.as_id);
441
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)));
448 }
449
450 // Have this host var, look up the object.
451 VarMap::iterator ret = live_vars_.find(found->second);
452
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());
456
457 // All objects should be proxy objects.
458 DCHECK(ret->second.var->AsProxyObjectVar());
459 return scoped_refptr<ProxyObjectVar>(ret->second.var->AsProxyObjectVar());
460 }
461
TrackSharedMemoryHandle(PP_Instance instance,base::SharedMemoryHandle handle,uint32 size_in_bytes)462 int PluginVarTracker::TrackSharedMemoryHandle(PP_Instance instance,
463 base::SharedMemoryHandle handle,
464 uint32 size_in_bytes) {
465 NOTREACHED();
466 return -1;
467 }
468
StopTrackingSharedMemoryHandle(int id,PP_Instance instance,base::SharedMemoryHandle * handle,uint32 * size_in_bytes)469 bool PluginVarTracker::StopTrackingSharedMemoryHandle(
470 int id,
471 PP_Instance instance,
472 base::SharedMemoryHandle* handle,
473 uint32* size_in_bytes) {
474 NOTREACHED();
475 return false;
476 }
477
478 } // namesace proxy
479 } // namespace ppapi
480