plugin_var_tracker.cc revision c2e0dbddbe15c98d52c4786dac06cb8952a8ae6d
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() : VarTracker(THREAD_SAFE) { 35} 36 37PluginVarTracker::~PluginVarTracker() { 38} 39 40PP_Var PluginVarTracker::ReceiveObjectPassRef(const PP_Var& host_var, 41 PluginDispatcher* dispatcher) { 42 CheckThreadingPreconditions(); 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 CheckThreadingPreconditions(); 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 CheckThreadingPreconditions(); 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 CheckThreadingPreconditions(); 102 if (plugin_object.type != PP_VARTYPE_OBJECT) { 103 NOTREACHED(); 104 return PP_MakeUndefined(); 105 } 106 107 Var* var = GetVar(plugin_object); 108 ProxyObjectVar* object = var->AsProxyObjectVar(); 109 if (!object) { 110 NOTREACHED(); 111 return PP_MakeUndefined(); 112 } 113 114 // Make a var with the host ID. 115 PP_Var ret = { PP_VARTYPE_OBJECT }; 116 ret.value.as_id = object->host_var_id(); 117 return ret; 118} 119 120PluginDispatcher* PluginVarTracker::DispatcherForPluginObject( 121 const PP_Var& plugin_object) const { 122 CheckThreadingPreconditions(); 123 if (plugin_object.type != PP_VARTYPE_OBJECT) 124 return NULL; 125 126 VarMap::const_iterator found = GetLiveVar(plugin_object); 127 if (found == live_vars_.end()) 128 return NULL; 129 130 ProxyObjectVar* object = found->second.var->AsProxyObjectVar(); 131 if (!object) 132 return NULL; 133 return object->dispatcher(); 134} 135 136void PluginVarTracker::ReleaseHostObject(PluginDispatcher* dispatcher, 137 const PP_Var& host_object) { 138 CheckThreadingPreconditions(); 139 DCHECK(host_object.type == PP_VARTYPE_OBJECT); 140 141 // Convert the host object to a normal var valid in the plugin. 142 HostVarToPluginVarMap::iterator found = host_var_to_plugin_var_.find( 143 HostVar(dispatcher, static_cast<int32>(host_object.value.as_id))); 144 if (found == host_var_to_plugin_var_.end()) { 145 NOTREACHED(); 146 return; 147 } 148 149 // Now just release the object given the plugin var ID. 150 ReleaseVar(found->second); 151} 152 153void PluginVarTracker::DidDeleteInstance(PP_Instance instance) { 154 // Calling the destructors on plugin objects may in turn release other 155 // objects which will mutate the map out from under us. So do a two-step 156 // process of identifying the ones to delete, and then delete them. 157 // 158 // See the comment above user_data_to_plugin_ in the header file. We assume 159 // there aren't that many objects so a brute-force search is reasonable. 160 std::vector<void*> user_data_to_delete; 161 for (UserDataToPluginImplementedVarMap::const_iterator i = 162 user_data_to_plugin_.begin(); 163 i != user_data_to_plugin_.end(); 164 ++i) { 165 if (i->second.instance == instance) 166 user_data_to_delete.push_back(i->first); 167 } 168 169 for (size_t i = 0; i < user_data_to_delete.size(); i++) { 170 UserDataToPluginImplementedVarMap::iterator found = 171 user_data_to_plugin_.find(user_data_to_delete[i]); 172 if (found == user_data_to_plugin_.end()) 173 continue; // Object removed from list while we were iterating. 174 175 if (!found->second.plugin_object_id) { 176 // This object is for the freed instance and the plugin is not holding 177 // any references to it. Deallocate immediately. 178 found->second.ppp_class->Deallocate(found->first); 179 user_data_to_plugin_.erase(found); 180 } else { 181 // The plugin is holding refs to this object. We don't want to call 182 // Deallocate since the plugin may be depending on those refs to keep 183 // its data alive. To avoid crashes in this case, just clear out the 184 // instance to mark it and continue. When the plugin refs go to 0, 185 // we'll notice there is no instance and call Deallocate. 186 found->second.instance = 0; 187 } 188 } 189} 190 191void PluginVarTracker::DidDeleteDispatcher(PluginDispatcher* dispatcher) { 192 for (size_t i = 0; i < live_vars_.size(); ++i) { 193 if (live_vars_[i].var.get() == NULL) 194 continue; 195 ProxyObjectVar* object = live_vars_[i].var->AsProxyObjectVar(); 196 if (object && object->dispatcher() == dispatcher) 197 object->clear_dispatcher(); 198 } 199} 200 201ArrayBufferVar* PluginVarTracker::CreateArrayBuffer(uint32 size_in_bytes) { 202 return new PluginArrayBufferVar(size_in_bytes); 203} 204 205ArrayBufferVar* PluginVarTracker::CreateShmArrayBuffer( 206 uint32 size_in_bytes, 207 base::SharedMemoryHandle handle) { 208 return new PluginArrayBufferVar(size_in_bytes, handle); 209} 210 211void PluginVarTracker::PluginImplementedObjectCreated( 212 PP_Instance instance, 213 const PP_Var& created_var, 214 const PPP_Class_Deprecated* ppp_class, 215 void* ppp_class_data) { 216 PluginImplementedVar p; 217 p.ppp_class = ppp_class; 218 p.instance = instance; 219 p.plugin_object_id = created_var.value.as_id; 220 user_data_to_plugin_[ppp_class_data] = p; 221 222 // Link the user data to the object. 223 ProxyObjectVar* object = GetVar(created_var)->AsProxyObjectVar(); 224 object->set_user_data(ppp_class_data); 225} 226 227void PluginVarTracker::PluginImplementedObjectDestroyed(void* user_data) { 228 UserDataToPluginImplementedVarMap::iterator found = 229 user_data_to_plugin_.find(user_data); 230 if (found == user_data_to_plugin_.end()) { 231 NOTREACHED(); 232 return; 233 } 234 user_data_to_plugin_.erase(found); 235} 236 237bool PluginVarTracker::IsPluginImplementedObjectAlive(void* user_data) { 238 return user_data_to_plugin_.find(user_data) != user_data_to_plugin_.end(); 239} 240 241bool PluginVarTracker::ValidatePluginObjectCall( 242 const PPP_Class_Deprecated* ppp_class, 243 void* user_data) { 244 UserDataToPluginImplementedVarMap::iterator found = 245 user_data_to_plugin_.find(user_data); 246 if (found == user_data_to_plugin_.end()) 247 return false; 248 return found->second.ppp_class == ppp_class; 249} 250 251int32 PluginVarTracker::AddVarInternal(Var* var, AddVarRefMode mode) { 252 // Normal adding. 253 int32 new_id = VarTracker::AddVarInternal(var, mode); 254 255 // Need to add proxy objects to the host var map. 256 ProxyObjectVar* proxy_object = var->AsProxyObjectVar(); 257 if (proxy_object) { 258 HostVar host_var(proxy_object->dispatcher(), proxy_object->host_var_id()); 259 DCHECK(host_var_to_plugin_var_.find(host_var) == 260 host_var_to_plugin_var_.end()); // Adding an object twice, use 261 // FindOrMakePluginVarFromHostVar. 262 host_var_to_plugin_var_[host_var] = new_id; 263 } 264 return new_id; 265} 266 267void PluginVarTracker::TrackedObjectGettingOneRef(VarMap::const_iterator iter) { 268 ProxyObjectVar* object = iter->second.var->AsProxyObjectVar(); 269 if (!object) { 270 NOTREACHED(); 271 return; 272 } 273 274 DCHECK(iter->second.ref_count == 0); 275 276 // Got an AddRef for an object we have no existing reference for. 277 // We need to tell the browser we've taken a ref. This comes up when the 278 // browser passes an object as an input param and holds a ref for us. 279 // This must be a sync message since otherwise the "addref" will actually 280 // occur after the return to the browser of the sync function that 281 // presumably sent the object. 282 SendAddRefObjectMsg(*object); 283} 284 285void PluginVarTracker::ObjectGettingZeroRef(VarMap::iterator iter) { 286 ProxyObjectVar* object = iter->second.var->AsProxyObjectVar(); 287 if (!object) { 288 NOTREACHED(); 289 return; 290 } 291 292 // Notify the host we're no longer holding our ref. 293 DCHECK(iter->second.ref_count == 0); 294 SendReleaseObjectMsg(*object); 295 296 UserDataToPluginImplementedVarMap::iterator found = 297 user_data_to_plugin_.find(object->user_data()); 298 if (found != user_data_to_plugin_.end()) { 299 // This object is implemented in the plugin. 300 if (found->second.instance == 0) { 301 // Instance is destroyed. This means that we'll never get a Deallocate 302 // call from the renderer and we should do so now. 303 found->second.ppp_class->Deallocate(found->first); 304 user_data_to_plugin_.erase(found); 305 } else { 306 // The plugin is releasing its last reference to an object it implements. 307 // Clear the tracking data that links our "plugin implemented object" to 308 // the var. If the instance is destroyed and there is no ID, we know that 309 // we should just call Deallocate on the object data. 310 // 311 // See the plugin_object_id declaration for more info. 312 found->second.plugin_object_id = 0; 313 } 314 } 315 316 // This will optionally delete the info from live_vars_. 317 VarTracker::ObjectGettingZeroRef(iter); 318} 319 320bool PluginVarTracker::DeleteObjectInfoIfNecessary(VarMap::iterator iter) { 321 // Get the info before calling the base class's version of this function, 322 // which may delete the object. 323 ProxyObjectVar* object = iter->second.var->AsProxyObjectVar(); 324 HostVar host_var(object->dispatcher(), object->host_var_id()); 325 326 if (!VarTracker::DeleteObjectInfoIfNecessary(iter)) 327 return false; 328 329 // Clean up the host var mapping. 330 DCHECK(host_var_to_plugin_var_.find(host_var) != 331 host_var_to_plugin_var_.end()); 332 host_var_to_plugin_var_.erase(host_var); 333 return true; 334} 335 336PP_Var PluginVarTracker::GetOrCreateObjectVarID(ProxyObjectVar* object) { 337 // We can't use object->GetPPVar() because we don't want to affect the 338 // refcount, so we have to add everything manually here. 339 int32 var_id = object->GetExistingVarID(); 340 if (!var_id) { 341 var_id = AddVarInternal(object, ADD_VAR_CREATE_WITH_NO_REFERENCE); 342 object->AssignVarID(var_id); 343 } 344 345 PP_Var ret = { PP_VARTYPE_OBJECT }; 346 ret.value.as_id = var_id; 347 return ret; 348} 349 350void PluginVarTracker::SendAddRefObjectMsg( 351 const ProxyObjectVar& proxy_object) { 352 int unused; 353 if (proxy_object.dispatcher()) { 354 proxy_object.dispatcher()->Send(new PpapiHostMsg_PPBVar_AddRefObject( 355 API_ID_PPB_VAR_DEPRECATED, proxy_object.host_var_id(), &unused)); 356 } 357} 358 359void PluginVarTracker::SendReleaseObjectMsg( 360 const ProxyObjectVar& proxy_object) { 361 if (proxy_object.dispatcher()) { 362 proxy_object.dispatcher()->Send(new PpapiHostMsg_PPBVar_ReleaseObject( 363 API_ID_PPB_VAR_DEPRECATED, proxy_object.host_var_id())); 364 } 365} 366 367scoped_refptr<ProxyObjectVar> PluginVarTracker::FindOrMakePluginVarFromHostVar( 368 const PP_Var& var, 369 PluginDispatcher* dispatcher) { 370 DCHECK(var.type == PP_VARTYPE_OBJECT); 371 HostVar host_var(dispatcher, var.value.as_id); 372 373 HostVarToPluginVarMap::iterator found = 374 host_var_to_plugin_var_.find(host_var); 375 if (found == host_var_to_plugin_var_.end()) { 376 // Create a new object. 377 return scoped_refptr<ProxyObjectVar>( 378 new ProxyObjectVar(dispatcher, static_cast<int32>(var.value.as_id))); 379 } 380 381 // Have this host var, look up the object. 382 VarMap::iterator ret = live_vars_.find(found->second); 383 DCHECK(ret != live_vars_.end()); 384 385 // All objects should be proxy objects. 386 DCHECK(ret->second.var->AsProxyObjectVar()); 387 return scoped_refptr<ProxyObjectVar>(ret->second.var->AsProxyObjectVar()); 388} 389 390int PluginVarTracker::TrackSharedMemoryHandle(PP_Instance instance, 391 base::SharedMemoryHandle handle, 392 uint32 size_in_bytes) { 393 NOTREACHED(); 394 return -1; 395} 396 397bool PluginVarTracker::StopTrackingSharedMemoryHandle( 398 int id, 399 PP_Instance instance, 400 base::SharedMemoryHandle* handle, 401 uint32* size_in_bytes) { 402 NOTREACHED(); 403 return false; 404} 405 406} // namesace proxy 407} // namespace ppapi 408