plugin_var_tracker.cc revision b2df76ea8fec9e32f6f3718986dba0d95315b29c
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 (VarMap::iterator it = live_vars_.begin(); 193 it != live_vars_.end(); 194 ++it) { 195 if (it->second.var.get() == NULL) 196 continue; 197 ProxyObjectVar* object = it->second.var->AsProxyObjectVar(); 198 if (object && object->dispatcher() == dispatcher) 199 object->clear_dispatcher(); 200 } 201} 202 203ArrayBufferVar* PluginVarTracker::CreateArrayBuffer(uint32 size_in_bytes) { 204 return new PluginArrayBufferVar(size_in_bytes); 205} 206 207ArrayBufferVar* PluginVarTracker::CreateShmArrayBuffer( 208 uint32 size_in_bytes, 209 base::SharedMemoryHandle handle) { 210 return new PluginArrayBufferVar(size_in_bytes, handle); 211} 212 213void PluginVarTracker::PluginImplementedObjectCreated( 214 PP_Instance instance, 215 const PP_Var& created_var, 216 const PPP_Class_Deprecated* ppp_class, 217 void* ppp_class_data) { 218 PluginImplementedVar p; 219 p.ppp_class = ppp_class; 220 p.instance = instance; 221 p.plugin_object_id = created_var.value.as_id; 222 user_data_to_plugin_[ppp_class_data] = p; 223 224 // Link the user data to the object. 225 ProxyObjectVar* object = GetVar(created_var)->AsProxyObjectVar(); 226 object->set_user_data(ppp_class_data); 227} 228 229void PluginVarTracker::PluginImplementedObjectDestroyed(void* user_data) { 230 UserDataToPluginImplementedVarMap::iterator found = 231 user_data_to_plugin_.find(user_data); 232 if (found == user_data_to_plugin_.end()) { 233 NOTREACHED(); 234 return; 235 } 236 user_data_to_plugin_.erase(found); 237} 238 239bool PluginVarTracker::IsPluginImplementedObjectAlive(void* user_data) { 240 return user_data_to_plugin_.find(user_data) != user_data_to_plugin_.end(); 241} 242 243bool PluginVarTracker::ValidatePluginObjectCall( 244 const PPP_Class_Deprecated* ppp_class, 245 void* user_data) { 246 UserDataToPluginImplementedVarMap::iterator found = 247 user_data_to_plugin_.find(user_data); 248 if (found == user_data_to_plugin_.end()) 249 return false; 250 return found->second.ppp_class == ppp_class; 251} 252 253int32 PluginVarTracker::AddVarInternal(Var* var, AddVarRefMode mode) { 254 // Normal adding. 255 int32 new_id = VarTracker::AddVarInternal(var, mode); 256 257 // Need to add proxy objects to the host var map. 258 ProxyObjectVar* proxy_object = var->AsProxyObjectVar(); 259 if (proxy_object) { 260 HostVar host_var(proxy_object->dispatcher(), proxy_object->host_var_id()); 261 DCHECK(host_var_to_plugin_var_.find(host_var) == 262 host_var_to_plugin_var_.end()); // Adding an object twice, use 263 // FindOrMakePluginVarFromHostVar. 264 host_var_to_plugin_var_[host_var] = new_id; 265 } 266 return new_id; 267} 268 269void PluginVarTracker::TrackedObjectGettingOneRef(VarMap::const_iterator iter) { 270 ProxyObjectVar* object = iter->second.var->AsProxyObjectVar(); 271 if (!object) { 272 NOTREACHED(); 273 return; 274 } 275 276 DCHECK(iter->second.ref_count == 0); 277 278 // Got an AddRef for an object we have no existing reference for. 279 // We need to tell the browser we've taken a ref. This comes up when the 280 // browser passes an object as an input param and holds a ref for us. 281 // This must be a sync message since otherwise the "addref" will actually 282 // occur after the return to the browser of the sync function that 283 // presumably sent the object. 284 SendAddRefObjectMsg(*object); 285} 286 287void PluginVarTracker::ObjectGettingZeroRef(VarMap::iterator iter) { 288 ProxyObjectVar* object = iter->second.var->AsProxyObjectVar(); 289 if (!object) { 290 NOTREACHED(); 291 return; 292 } 293 294 // Notify the host we're no longer holding our ref. 295 DCHECK(iter->second.ref_count == 0); 296 SendReleaseObjectMsg(*object); 297 298 UserDataToPluginImplementedVarMap::iterator found = 299 user_data_to_plugin_.find(object->user_data()); 300 if (found != user_data_to_plugin_.end()) { 301 // This object is implemented in the plugin. 302 if (found->second.instance == 0) { 303 // Instance is destroyed. This means that we'll never get a Deallocate 304 // call from the renderer and we should do so now. 305 found->second.ppp_class->Deallocate(found->first); 306 user_data_to_plugin_.erase(found); 307 } else { 308 // The plugin is releasing its last reference to an object it implements. 309 // Clear the tracking data that links our "plugin implemented object" to 310 // the var. If the instance is destroyed and there is no ID, we know that 311 // we should just call Deallocate on the object data. 312 // 313 // See the plugin_object_id declaration for more info. 314 found->second.plugin_object_id = 0; 315 } 316 } 317 318 // This will optionally delete the info from live_vars_. 319 VarTracker::ObjectGettingZeroRef(iter); 320} 321 322bool PluginVarTracker::DeleteObjectInfoIfNecessary(VarMap::iterator iter) { 323 // Get the info before calling the base class's version of this function, 324 // which may delete the object. 325 ProxyObjectVar* object = iter->second.var->AsProxyObjectVar(); 326 HostVar host_var(object->dispatcher(), object->host_var_id()); 327 328 if (!VarTracker::DeleteObjectInfoIfNecessary(iter)) 329 return false; 330 331 // Clean up the host var mapping. 332 DCHECK(host_var_to_plugin_var_.find(host_var) != 333 host_var_to_plugin_var_.end()); 334 host_var_to_plugin_var_.erase(host_var); 335 return true; 336} 337 338PP_Var PluginVarTracker::GetOrCreateObjectVarID(ProxyObjectVar* object) { 339 // We can't use object->GetPPVar() because we don't want to affect the 340 // refcount, so we have to add everything manually here. 341 int32 var_id = object->GetExistingVarID(); 342 if (!var_id) { 343 var_id = AddVarInternal(object, ADD_VAR_CREATE_WITH_NO_REFERENCE); 344 object->AssignVarID(var_id); 345 } 346 347 PP_Var ret = { PP_VARTYPE_OBJECT }; 348 ret.value.as_id = var_id; 349 return ret; 350} 351 352void PluginVarTracker::SendAddRefObjectMsg( 353 const ProxyObjectVar& proxy_object) { 354 int unused; 355 if (proxy_object.dispatcher()) { 356 proxy_object.dispatcher()->Send(new PpapiHostMsg_PPBVar_AddRefObject( 357 API_ID_PPB_VAR_DEPRECATED, proxy_object.host_var_id(), &unused)); 358 } 359} 360 361void PluginVarTracker::SendReleaseObjectMsg( 362 const ProxyObjectVar& proxy_object) { 363 if (proxy_object.dispatcher()) { 364 proxy_object.dispatcher()->Send(new PpapiHostMsg_PPBVar_ReleaseObject( 365 API_ID_PPB_VAR_DEPRECATED, proxy_object.host_var_id())); 366 } 367} 368 369scoped_refptr<ProxyObjectVar> PluginVarTracker::FindOrMakePluginVarFromHostVar( 370 const PP_Var& var, 371 PluginDispatcher* dispatcher) { 372 DCHECK(var.type == PP_VARTYPE_OBJECT); 373 HostVar host_var(dispatcher, var.value.as_id); 374 375 HostVarToPluginVarMap::iterator found = 376 host_var_to_plugin_var_.find(host_var); 377 if (found == host_var_to_plugin_var_.end()) { 378 // Create a new object. 379 return scoped_refptr<ProxyObjectVar>( 380 new ProxyObjectVar(dispatcher, static_cast<int32>(var.value.as_id))); 381 } 382 383 // Have this host var, look up the object. 384 VarMap::iterator ret = live_vars_.find(found->second); 385 DCHECK(ret != live_vars_.end()); 386 387 // All objects should be proxy objects. 388 DCHECK(ret->second.var->AsProxyObjectVar()); 389 return scoped_refptr<ProxyObjectVar>(ret->second.var->AsProxyObjectVar()); 390} 391 392int PluginVarTracker::TrackSharedMemoryHandle(PP_Instance instance, 393 base::SharedMemoryHandle handle, 394 uint32 size_in_bytes) { 395 NOTREACHED(); 396 return -1; 397} 398 399bool PluginVarTracker::StopTrackingSharedMemoryHandle( 400 int id, 401 PP_Instance instance, 402 base::SharedMemoryHandle* handle, 403 uint32* size_in_bytes) { 404 NOTREACHED(); 405 return false; 406} 407 408} // namesace proxy 409} // namespace ppapi 410