plugin_var_tracker.cc revision 2a99a7e74a7f215066514fe81d2bfa6639d9eddd
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 "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" 17 18namespace ppapi { 19namespace proxy { 20 21PluginVarTracker::HostVar::HostVar(PluginDispatcher* d, int32 i) 22 : dispatcher(d), 23 host_object_id(i) { 24} 25 26bool PluginVarTracker::HostVar::operator<(const HostVar& other) const { 27 if (dispatcher < other.dispatcher) 28 return true; 29 if (other.dispatcher < dispatcher) 30 return false; 31 return host_object_id < other.host_object_id; 32} 33 34PluginVarTracker::PluginVarTracker() { 35} 36 37PluginVarTracker::~PluginVarTracker() { 38} 39 40PP_Var PluginVarTracker::ReceiveObjectPassRef(const PP_Var& host_var, 41 PluginDispatcher* dispatcher) { 42 DCHECK(CalledOnValidThread()); 43 DCHECK(host_var.type == PP_VARTYPE_OBJECT); 44 45 // Get the object. 46 scoped_refptr<ProxyObjectVar> object( 47 FindOrMakePluginVarFromHostVar(host_var, dispatcher)); 48 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()); 52 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); 60 } 61 info.ref_count++; 62 return ret; 63} 64 65PP_Var PluginVarTracker::TrackObjectWithNoReference( 66 const PP_Var& host_var, 67 PluginDispatcher* dispatcher) { 68 DCHECK(CalledOnValidThread()); 69 DCHECK(host_var.type == PP_VARTYPE_OBJECT); 70 71 // Get the object. 72 scoped_refptr<ProxyObjectVar> object( 73 FindOrMakePluginVarFromHostVar(host_var, dispatcher)); 74 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()); 78 79 VarInfo& info = GetLiveVar(ret)->second; 80 info.track_with_no_reference_count++; 81 return ret; 82} 83 84void PluginVarTracker::StopTrackingObjectWithNoReference( 85 const PP_Var& plugin_var) { 86 DCHECK(CalledOnValidThread()); 87 DCHECK(plugin_var.type == PP_VARTYPE_OBJECT); 88 89 VarMap::iterator found = GetLiveVar(plugin_var); 90 if (found == live_vars_.end()) { 91 NOTREACHED(); 92 return; 93 } 94 95 DCHECK(found->second.track_with_no_reference_count > 0); 96 found->second.track_with_no_reference_count--; 97 DeleteObjectInfoIfNecessary(found); 98} 99 100PP_Var PluginVarTracker::GetHostObject(const PP_Var& plugin_object) const { 101 DCHECK(CalledOnValidThread()); 102 103 if (plugin_object.type != PP_VARTYPE_OBJECT) { 104 NOTREACHED(); 105 return PP_MakeUndefined(); 106 } 107 108 Var* var = GetVar(plugin_object); 109 ProxyObjectVar* object = var->AsProxyObjectVar(); 110 if (!object) { 111 NOTREACHED(); 112 return PP_MakeUndefined(); 113 } 114 115 // Make a var with the host ID. 116 PP_Var ret = { PP_VARTYPE_OBJECT }; 117 ret.value.as_id = object->host_var_id(); 118 return ret; 119} 120 121PluginDispatcher* PluginVarTracker::DispatcherForPluginObject( 122 const PP_Var& plugin_object) const { 123 DCHECK(CalledOnValidThread()); 124 125 if (plugin_object.type != PP_VARTYPE_OBJECT) 126 return NULL; 127 128 VarMap::const_iterator found = GetLiveVar(plugin_object); 129 if (found == live_vars_.end()) 130 return NULL; 131 132 ProxyObjectVar* object = found->second.var->AsProxyObjectVar(); 133 if (!object) 134 return NULL; 135 return object->dispatcher(); 136} 137 138void PluginVarTracker::ReleaseHostObject(PluginDispatcher* dispatcher, 139 const PP_Var& host_object) { 140 DCHECK(CalledOnValidThread()); 141 DCHECK(host_object.type == PP_VARTYPE_OBJECT); 142 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()) { 147 NOTREACHED(); 148 return; 149 } 150 151 // Now just release the object given the plugin var ID. 152 ReleaseVar(found->second); 153} 154 155void 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. 159 // 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(); 166 ++i) { 167 if (i->second.instance == instance) 168 user_data_to_delete.push_back(i->first); 169 } 170 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. 176 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); 182 } else { 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; 189 } 190 } 191} 192 193ArrayBufferVar* PluginVarTracker::CreateArrayBuffer(uint32 size_in_bytes) { 194 return new PluginArrayBufferVar(size_in_bytes); 195} 196 197ArrayBufferVar* PluginVarTracker::CreateShmArrayBuffer( 198 uint32 size_in_bytes, 199 base::SharedMemoryHandle handle) { 200 return new PluginArrayBufferVar(size_in_bytes, handle); 201} 202 203void PluginVarTracker::PluginImplementedObjectCreated( 204 PP_Instance instance, 205 const PP_Var& created_var, 206 const PPP_Class_Deprecated* ppp_class, 207 void* ppp_class_data) { 208 PluginImplementedVar p; 209 p.ppp_class = ppp_class; 210 p.instance = instance; 211 p.plugin_object_id = created_var.value.as_id; 212 user_data_to_plugin_[ppp_class_data] = p; 213 214 // Link the user data to the object. 215 ProxyObjectVar* object = GetVar(created_var)->AsProxyObjectVar(); 216 object->set_user_data(ppp_class_data); 217} 218 219void PluginVarTracker::PluginImplementedObjectDestroyed(void* user_data) { 220 UserDataToPluginImplementedVarMap::iterator found = 221 user_data_to_plugin_.find(user_data); 222 if (found == user_data_to_plugin_.end()) { 223 NOTREACHED(); 224 return; 225 } 226 user_data_to_plugin_.erase(found); 227} 228 229bool PluginVarTracker::IsPluginImplementedObjectAlive(void* user_data) { 230 return user_data_to_plugin_.find(user_data) != user_data_to_plugin_.end(); 231} 232 233bool PluginVarTracker::ValidatePluginObjectCall( 234 const PPP_Class_Deprecated* ppp_class, 235 void* user_data) { 236 UserDataToPluginImplementedVarMap::iterator found = 237 user_data_to_plugin_.find(user_data); 238 if (found == user_data_to_plugin_.end()) 239 return false; 240 return found->second.ppp_class == ppp_class; 241} 242 243int32 PluginVarTracker::AddVarInternal(Var* var, AddVarRefMode mode) { 244 // Normal adding. 245 int32 new_id = VarTracker::AddVarInternal(var, mode); 246 247 // Need to add proxy objects to the host var map. 248 ProxyObjectVar* proxy_object = var->AsProxyObjectVar(); 249 if (proxy_object) { 250 HostVar host_var(proxy_object->dispatcher(), proxy_object->host_var_id()); 251 DCHECK(host_var_to_plugin_var_.find(host_var) == 252 host_var_to_plugin_var_.end()); // Adding an object twice, use 253 // FindOrMakePluginVarFromHostVar. 254 host_var_to_plugin_var_[host_var] = new_id; 255 } 256 return new_id; 257} 258 259void PluginVarTracker::TrackedObjectGettingOneRef(VarMap::const_iterator iter) { 260 ProxyObjectVar* object = iter->second.var->AsProxyObjectVar(); 261 if (!object) { 262 NOTREACHED(); 263 return; 264 } 265 266 DCHECK(iter->second.ref_count == 0); 267 268 // Got an AddRef for an object we have no existing reference for. 269 // We need to tell the browser we've taken a ref. This comes up when the 270 // browser passes an object as an input param and holds a ref for us. 271 // This must be a sync message since otherwise the "addref" will actually 272 // occur after the return to the browser of the sync function that 273 // presumably sent the object. 274 SendAddRefObjectMsg(*object); 275} 276 277void PluginVarTracker::ObjectGettingZeroRef(VarMap::iterator iter) { 278 ProxyObjectVar* object = iter->second.var->AsProxyObjectVar(); 279 if (!object) { 280 NOTREACHED(); 281 return; 282 } 283 284 // Notify the host we're no longer holding our ref. 285 DCHECK(iter->second.ref_count == 0); 286 SendReleaseObjectMsg(*object); 287 288 UserDataToPluginImplementedVarMap::iterator found = 289 user_data_to_plugin_.find(object->user_data()); 290 if (found != user_data_to_plugin_.end()) { 291 // This object is implemented in the plugin. 292 if (found->second.instance == 0) { 293 // Instance is destroyed. This means that we'll never get a Deallocate 294 // call from the renderer and we should do so now. 295 found->second.ppp_class->Deallocate(found->first); 296 user_data_to_plugin_.erase(found); 297 } else { 298 // The plugin is releasing its last reference to an object it implements. 299 // Clear the tracking data that links our "plugin implemented object" to 300 // the var. If the instance is destroyed and there is no ID, we know that 301 // we should just call Deallocate on the object data. 302 // 303 // See the plugin_object_id declaration for more info. 304 found->second.plugin_object_id = 0; 305 } 306 } 307 308 // This will optionally delete the info from live_vars_. 309 VarTracker::ObjectGettingZeroRef(iter); 310} 311 312bool PluginVarTracker::DeleteObjectInfoIfNecessary(VarMap::iterator iter) { 313 // Get the info before calling the base class's version of this function, 314 // which may delete the object. 315 ProxyObjectVar* object = iter->second.var->AsProxyObjectVar(); 316 HostVar host_var(object->dispatcher(), object->host_var_id()); 317 318 if (!VarTracker::DeleteObjectInfoIfNecessary(iter)) 319 return false; 320 321 // Clean up the host var mapping. 322 DCHECK(host_var_to_plugin_var_.find(host_var) != 323 host_var_to_plugin_var_.end()); 324 host_var_to_plugin_var_.erase(host_var); 325 return true; 326} 327 328PP_Var PluginVarTracker::GetOrCreateObjectVarID(ProxyObjectVar* object) { 329 // We can't use object->GetPPVar() because we don't want to affect the 330 // refcount, so we have to add everything manually here. 331 int32 var_id = object->GetExistingVarID(); 332 if (!var_id) { 333 var_id = AddVarInternal(object, ADD_VAR_CREATE_WITH_NO_REFERENCE); 334 object->AssignVarID(var_id); 335 } 336 337 PP_Var ret = { PP_VARTYPE_OBJECT }; 338 ret.value.as_id = var_id; 339 return ret; 340} 341 342void PluginVarTracker::SendAddRefObjectMsg( 343 const ProxyObjectVar& proxy_object) { 344 int unused; 345 proxy_object.dispatcher()->Send(new PpapiHostMsg_PPBVar_AddRefObject( 346 API_ID_PPB_VAR_DEPRECATED, proxy_object.host_var_id(), &unused)); 347} 348 349void PluginVarTracker::SendReleaseObjectMsg( 350 const ProxyObjectVar& proxy_object) { 351 proxy_object.dispatcher()->Send(new PpapiHostMsg_PPBVar_ReleaseObject( 352 API_ID_PPB_VAR_DEPRECATED, proxy_object.host_var_id())); 353} 354 355scoped_refptr<ProxyObjectVar> PluginVarTracker::FindOrMakePluginVarFromHostVar( 356 const PP_Var& var, 357 PluginDispatcher* dispatcher) { 358 DCHECK(var.type == PP_VARTYPE_OBJECT); 359 HostVar host_var(dispatcher, var.value.as_id); 360 361 HostVarToPluginVarMap::iterator found = 362 host_var_to_plugin_var_.find(host_var); 363 if (found == host_var_to_plugin_var_.end()) { 364 // Create a new object. 365 return scoped_refptr<ProxyObjectVar>( 366 new ProxyObjectVar(dispatcher, static_cast<int32>(var.value.as_id))); 367 } 368 369 // Have this host var, look up the object. 370 VarMap::iterator ret = live_vars_.find(found->second); 371 DCHECK(ret != live_vars_.end()); 372 373 // All objects should be proxy objects. 374 DCHECK(ret->second.var->AsProxyObjectVar()); 375 return scoped_refptr<ProxyObjectVar>(ret->second.var->AsProxyObjectVar()); 376} 377 378int PluginVarTracker::TrackSharedMemoryHandle(PP_Instance instance, 379 base::SharedMemoryHandle handle, 380 uint32 size_in_bytes) { 381 NOTREACHED(); 382 return -1; 383} 384 385bool PluginVarTracker::StopTrackingSharedMemoryHandle( 386 int id, 387 PP_Instance instance, 388 base::SharedMemoryHandle* handle, 389 uint32* size_in_bytes) { 390 NOTREACHED(); 391 return false; 392} 393 394} // namesace proxy 395} // namespace ppapi 396