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 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 38 PluginVarTracker::HostVar::HostVar(PluginDispatcher* d, int32 i) 39 : dispatcher(d), 40 host_object_id(i) { 41 } 42 43 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 51 PluginVarTracker::PluginVarTracker() : VarTracker(THREAD_SAFE) { 52 } 53 54 PluginVarTracker::~PluginVarTracker() { 55 } 56 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 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 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 117 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 137 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 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 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 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 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 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 268 ArrayBufferVar* PluginVarTracker::CreateArrayBuffer(uint32 size_in_bytes) { 269 return new PluginArrayBufferVar(size_in_bytes); 270 } 271 272 ArrayBufferVar* PluginVarTracker::CreateShmArrayBuffer( 273 uint32 size_in_bytes, 274 base::SharedMemoryHandle handle) { 275 return new PluginArrayBufferVar(size_in_bytes, handle); 276 } 277 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 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 304 bool PluginVarTracker::IsPluginImplementedObjectAlive(void* user_data) { 305 return user_data_to_plugin_.find(user_data) != user_data_to_plugin_.end(); 306 } 307 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 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 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 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 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 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 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 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 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 462 int PluginVarTracker::TrackSharedMemoryHandle(PP_Instance instance, 463 base::SharedMemoryHandle handle, 464 uint32 size_in_bytes) { 465 NOTREACHED(); 466 return -1; 467 } 468 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