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/media_stream_audio_track_resource.h" 14 #include "ppapi/proxy/media_stream_video_track_resource.h" 15 #include "ppapi/proxy/plugin_array_buffer_var.h" 16 #include "ppapi/proxy/plugin_dispatcher.h" 17 #include "ppapi/proxy/plugin_globals.h" 18 #include "ppapi/proxy/plugin_resource_var.h" 19 #include "ppapi/proxy/ppapi_messages.h" 20 #include "ppapi/proxy/proxy_object_var.h" 21 #include "ppapi/shared_impl/api_id.h" 22 #include "ppapi/shared_impl/ppapi_globals.h" 23 #include "ppapi/shared_impl/proxy_lock.h" 24 #include "ppapi/shared_impl/resource_tracker.h" 25 #include "ppapi/shared_impl/var.h" 26 27 namespace ppapi { 28 namespace proxy { 29 30 namespace { 31 32 Connection GetConnectionForInstance(PP_Instance instance) { 33 PluginDispatcher* dispatcher = PluginDispatcher::GetForInstance(instance); 34 DCHECK(dispatcher); 35 return Connection(PluginGlobals::Get()->GetBrowserSender(), dispatcher); 36 } 37 38 } // namespace 39 40 PluginVarTracker::HostVar::HostVar(PluginDispatcher* d, int32 i) 41 : dispatcher(d), 42 host_object_id(i) { 43 } 44 45 bool PluginVarTracker::HostVar::operator<(const HostVar& other) const { 46 if (dispatcher < other.dispatcher) 47 return true; 48 if (other.dispatcher < dispatcher) 49 return false; 50 return host_object_id < other.host_object_id; 51 } 52 53 PluginVarTracker::PluginVarTracker() : VarTracker(THREAD_SAFE) { 54 } 55 56 PluginVarTracker::~PluginVarTracker() { 57 } 58 59 PP_Var PluginVarTracker::ReceiveObjectPassRef(const PP_Var& host_var, 60 PluginDispatcher* dispatcher) { 61 CheckThreadingPreconditions(); 62 DCHECK(host_var.type == PP_VARTYPE_OBJECT); 63 64 // Get the object. 65 scoped_refptr<ProxyObjectVar> object( 66 FindOrMakePluginVarFromHostVar(host_var, dispatcher)); 67 68 // Actually create the PP_Var, this will add all the tracking info but not 69 // adjust any refcounts. 70 PP_Var ret = GetOrCreateObjectVarID(object.get()); 71 72 VarInfo& info = GetLiveVar(ret)->second; 73 if (info.ref_count > 0) { 74 // We already had a reference to it before. That means the renderer now has 75 // two references on our behalf. We want to transfer that extra reference 76 // to our list. This means we addref in the plugin, and release the extra 77 // one in the renderer. 78 SendReleaseObjectMsg(*object.get()); 79 } 80 info.ref_count++; 81 return ret; 82 } 83 84 PP_Var PluginVarTracker::TrackObjectWithNoReference( 85 const PP_Var& host_var, 86 PluginDispatcher* dispatcher) { 87 CheckThreadingPreconditions(); 88 DCHECK(host_var.type == PP_VARTYPE_OBJECT); 89 90 // Get the object. 91 scoped_refptr<ProxyObjectVar> object( 92 FindOrMakePluginVarFromHostVar(host_var, dispatcher)); 93 94 // Actually create the PP_Var, this will add all the tracking info but not 95 // adjust any refcounts. 96 PP_Var ret = GetOrCreateObjectVarID(object.get()); 97 98 VarInfo& info = GetLiveVar(ret)->second; 99 info.track_with_no_reference_count++; 100 return ret; 101 } 102 103 void PluginVarTracker::StopTrackingObjectWithNoReference( 104 const PP_Var& plugin_var) { 105 CheckThreadingPreconditions(); 106 DCHECK(plugin_var.type == PP_VARTYPE_OBJECT); 107 108 VarMap::iterator found = GetLiveVar(plugin_var); 109 if (found == live_vars_.end()) { 110 NOTREACHED(); 111 return; 112 } 113 114 DCHECK(found->second.track_with_no_reference_count > 0); 115 found->second.track_with_no_reference_count--; 116 DeleteObjectInfoIfNecessary(found); 117 } 118 119 PP_Var PluginVarTracker::GetHostObject(const PP_Var& plugin_object) const { 120 CheckThreadingPreconditions(); 121 if (plugin_object.type != PP_VARTYPE_OBJECT) { 122 NOTREACHED(); 123 return PP_MakeUndefined(); 124 } 125 126 Var* var = GetVar(plugin_object); 127 ProxyObjectVar* object = var->AsProxyObjectVar(); 128 if (!object) { 129 NOTREACHED(); 130 return PP_MakeUndefined(); 131 } 132 133 // Make a var with the host ID. 134 PP_Var ret = { PP_VARTYPE_OBJECT }; 135 ret.value.as_id = object->host_var_id(); 136 return ret; 137 } 138 139 PluginDispatcher* PluginVarTracker::DispatcherForPluginObject( 140 const PP_Var& plugin_object) const { 141 CheckThreadingPreconditions(); 142 if (plugin_object.type != PP_VARTYPE_OBJECT) 143 return NULL; 144 145 VarMap::const_iterator found = GetLiveVar(plugin_object); 146 if (found == live_vars_.end()) 147 return NULL; 148 149 ProxyObjectVar* object = found->second.var->AsProxyObjectVar(); 150 if (!object) 151 return NULL; 152 return object->dispatcher(); 153 } 154 155 void PluginVarTracker::ReleaseHostObject(PluginDispatcher* dispatcher, 156 const PP_Var& host_object) { 157 CheckThreadingPreconditions(); 158 DCHECK(host_object.type == PP_VARTYPE_OBJECT); 159 160 // Convert the host object to a normal var valid in the plugin. 161 HostVarToPluginVarMap::iterator found = host_var_to_plugin_var_.find( 162 HostVar(dispatcher, static_cast<int32>(host_object.value.as_id))); 163 if (found == host_var_to_plugin_var_.end()) { 164 NOTREACHED(); 165 return; 166 } 167 168 // Now just release the object given the plugin var ID. 169 ReleaseVar(found->second); 170 } 171 172 PP_Var PluginVarTracker::MakeResourcePPVarFromMessage( 173 PP_Instance instance, 174 const IPC::Message& creation_message, 175 int pending_renderer_id, 176 int pending_browser_id) { 177 switch (creation_message.type()) { 178 case PpapiPluginMsg_FileSystem_CreateFromPendingHost::ID: { 179 DCHECK(pending_renderer_id); 180 DCHECK(pending_browser_id); 181 PP_FileSystemType file_system_type; 182 if (!UnpackMessage<PpapiPluginMsg_FileSystem_CreateFromPendingHost>( 183 creation_message, &file_system_type)) { 184 NOTREACHED() << "Invalid message of type " 185 "PpapiPluginMsg_FileSystem_CreateFromPendingHost"; 186 return PP_MakeNull(); 187 } 188 // Create a plugin-side resource and attach it to the host resource. 189 // Note: This only makes sense when the plugin is out of process (which 190 // should always be true when passing resource vars). 191 PP_Resource pp_resource = 192 (new FileSystemResource(GetConnectionForInstance(instance), 193 instance, 194 pending_renderer_id, 195 pending_browser_id, 196 file_system_type))->GetReference(); 197 return MakeResourcePPVar(pp_resource); 198 } 199 case PpapiPluginMsg_MediaStreamAudioTrack_CreateFromPendingHost::ID: { 200 DCHECK(pending_renderer_id); 201 std::string track_id; 202 if (!UnpackMessage< 203 PpapiPluginMsg_MediaStreamAudioTrack_CreateFromPendingHost>( 204 creation_message, &track_id)) { 205 NOTREACHED() << 206 "Invalid message of type " 207 "PpapiPluginMsg_MediaStreamAudioTrack_CreateFromPendingHost"; 208 return PP_MakeNull(); 209 } 210 PP_Resource pp_resource = 211 (new MediaStreamAudioTrackResource(GetConnectionForInstance(instance), 212 instance, 213 pending_renderer_id, 214 track_id))->GetReference(); 215 return MakeResourcePPVar(pp_resource); 216 } 217 case PpapiPluginMsg_MediaStreamVideoTrack_CreateFromPendingHost::ID: { 218 DCHECK(pending_renderer_id); 219 std::string track_id; 220 if (!UnpackMessage< 221 PpapiPluginMsg_MediaStreamVideoTrack_CreateFromPendingHost>( 222 creation_message, &track_id)) { 223 NOTREACHED() << 224 "Invalid message of type " 225 "PpapiPluginMsg_MediaStreamVideoTrack_CreateFromPendingHost"; 226 return PP_MakeNull(); 227 } 228 PP_Resource pp_resource = 229 (new MediaStreamVideoTrackResource(GetConnectionForInstance(instance), 230 instance, 231 pending_renderer_id, 232 track_id))->GetReference(); 233 return MakeResourcePPVar(pp_resource); 234 } 235 default: { 236 NOTREACHED() << "Creation message has unexpected type " 237 << creation_message.type(); 238 return PP_MakeNull(); 239 } 240 } 241 } 242 243 ResourceVar* PluginVarTracker::MakeResourceVar(PP_Resource pp_resource) { 244 // The resource 0 returns a null resource var. 245 if (!pp_resource) 246 return new PluginResourceVar(); 247 248 ResourceTracker* resource_tracker = PpapiGlobals::Get()->GetResourceTracker(); 249 ppapi::Resource* resource = resource_tracker->GetResource(pp_resource); 250 // A non-existant resource other than 0 returns NULL. 251 if (!resource) 252 return NULL; 253 return new PluginResourceVar(resource); 254 } 255 256 void PluginVarTracker::DidDeleteInstance(PP_Instance instance) { 257 // Calling the destructors on plugin objects may in turn release other 258 // objects which will mutate the map out from under us. So do a two-step 259 // process of identifying the ones to delete, and then delete them. 260 // 261 // See the comment above user_data_to_plugin_ in the header file. We assume 262 // there aren't that many objects so a brute-force search is reasonable. 263 std::vector<void*> user_data_to_delete; 264 for (UserDataToPluginImplementedVarMap::const_iterator i = 265 user_data_to_plugin_.begin(); 266 i != user_data_to_plugin_.end(); 267 ++i) { 268 if (i->second.instance == instance) 269 user_data_to_delete.push_back(i->first); 270 } 271 272 for (size_t i = 0; i < user_data_to_delete.size(); i++) { 273 UserDataToPluginImplementedVarMap::iterator found = 274 user_data_to_plugin_.find(user_data_to_delete[i]); 275 if (found == user_data_to_plugin_.end()) 276 continue; // Object removed from list while we were iterating. 277 278 if (!found->second.plugin_object_id) { 279 // This object is for the freed instance and the plugin is not holding 280 // any references to it. Deallocate immediately. 281 CallWhileUnlocked(found->second.ppp_class->Deallocate, found->first); 282 user_data_to_plugin_.erase(found); 283 } else { 284 // The plugin is holding refs to this object. We don't want to call 285 // Deallocate since the plugin may be depending on those refs to keep 286 // its data alive. To avoid crashes in this case, just clear out the 287 // instance to mark it and continue. When the plugin refs go to 0, 288 // we'll notice there is no instance and call Deallocate. 289 found->second.instance = 0; 290 } 291 } 292 } 293 294 void PluginVarTracker::DidDeleteDispatcher(PluginDispatcher* dispatcher) { 295 for (VarMap::iterator it = live_vars_.begin(); 296 it != live_vars_.end(); 297 ++it) { 298 if (it->second.var.get() == NULL) 299 continue; 300 ProxyObjectVar* object = it->second.var->AsProxyObjectVar(); 301 if (object && object->dispatcher() == dispatcher) 302 object->clear_dispatcher(); 303 } 304 } 305 306 ArrayBufferVar* PluginVarTracker::CreateArrayBuffer(uint32 size_in_bytes) { 307 return new PluginArrayBufferVar(size_in_bytes); 308 } 309 310 ArrayBufferVar* PluginVarTracker::CreateShmArrayBuffer( 311 uint32 size_in_bytes, 312 base::SharedMemoryHandle handle) { 313 return new PluginArrayBufferVar(size_in_bytes, handle); 314 } 315 316 void PluginVarTracker::PluginImplementedObjectCreated( 317 PP_Instance instance, 318 const PP_Var& created_var, 319 const PPP_Class_Deprecated* ppp_class, 320 void* ppp_class_data) { 321 PluginImplementedVar p; 322 p.ppp_class = ppp_class; 323 p.instance = instance; 324 p.plugin_object_id = created_var.value.as_id; 325 user_data_to_plugin_[ppp_class_data] = p; 326 327 // Link the user data to the object. 328 ProxyObjectVar* object = GetVar(created_var)->AsProxyObjectVar(); 329 object->set_user_data(ppp_class_data); 330 } 331 332 void PluginVarTracker::PluginImplementedObjectDestroyed(void* user_data) { 333 UserDataToPluginImplementedVarMap::iterator found = 334 user_data_to_plugin_.find(user_data); 335 if (found == user_data_to_plugin_.end()) { 336 NOTREACHED(); 337 return; 338 } 339 user_data_to_plugin_.erase(found); 340 } 341 342 bool PluginVarTracker::IsPluginImplementedObjectAlive(void* user_data) { 343 return user_data_to_plugin_.find(user_data) != user_data_to_plugin_.end(); 344 } 345 346 bool PluginVarTracker::ValidatePluginObjectCall( 347 const PPP_Class_Deprecated* ppp_class, 348 void* user_data) { 349 UserDataToPluginImplementedVarMap::iterator found = 350 user_data_to_plugin_.find(user_data); 351 if (found == user_data_to_plugin_.end()) 352 return false; 353 return found->second.ppp_class == ppp_class; 354 } 355 356 int32 PluginVarTracker::AddVarInternal(Var* var, AddVarRefMode mode) { 357 // Normal adding. 358 int32 new_id = VarTracker::AddVarInternal(var, mode); 359 360 // Need to add proxy objects to the host var map. 361 ProxyObjectVar* proxy_object = var->AsProxyObjectVar(); 362 if (proxy_object) { 363 HostVar host_var(proxy_object->dispatcher(), proxy_object->host_var_id()); 364 // TODO(teravest): Change to DCHECK when http://crbug.com/276347 is 365 // resolved. 366 CHECK(host_var_to_plugin_var_.find(host_var) == 367 host_var_to_plugin_var_.end()); // Adding an object twice, use 368 // FindOrMakePluginVarFromHostVar. 369 host_var_to_plugin_var_[host_var] = new_id; 370 } 371 return new_id; 372 } 373 374 void PluginVarTracker::TrackedObjectGettingOneRef(VarMap::const_iterator iter) { 375 ProxyObjectVar* object = iter->second.var->AsProxyObjectVar(); 376 if (!object) { 377 NOTREACHED(); 378 return; 379 } 380 381 DCHECK(iter->second.ref_count == 0); 382 383 // Got an AddRef for an object we have no existing reference for. 384 // We need to tell the browser we've taken a ref. This comes up when the 385 // browser passes an object as an input param and holds a ref for us. 386 // This must be a sync message since otherwise the "addref" will actually 387 // occur after the return to the browser of the sync function that 388 // presumably sent the object. 389 SendAddRefObjectMsg(*object); 390 } 391 392 void PluginVarTracker::ObjectGettingZeroRef(VarMap::iterator iter) { 393 ProxyObjectVar* object = iter->second.var->AsProxyObjectVar(); 394 if (!object) { 395 NOTREACHED(); 396 return; 397 } 398 399 // Notify the host we're no longer holding our ref. 400 DCHECK(iter->second.ref_count == 0); 401 SendReleaseObjectMsg(*object); 402 403 UserDataToPluginImplementedVarMap::iterator found = 404 user_data_to_plugin_.find(object->user_data()); 405 if (found != user_data_to_plugin_.end()) { 406 // This object is implemented in the plugin. 407 if (found->second.instance == 0) { 408 // Instance is destroyed. This means that we'll never get a Deallocate 409 // call from the renderer and we should do so now. 410 found->second.ppp_class->Deallocate(found->first); 411 user_data_to_plugin_.erase(found); 412 } else { 413 // The plugin is releasing its last reference to an object it implements. 414 // Clear the tracking data that links our "plugin implemented object" to 415 // the var. If the instance is destroyed and there is no ID, we know that 416 // we should just call Deallocate on the object data. 417 // 418 // See the plugin_object_id declaration for more info. 419 found->second.plugin_object_id = 0; 420 } 421 } 422 423 // This will optionally delete the info from live_vars_. 424 VarTracker::ObjectGettingZeroRef(iter); 425 } 426 427 bool PluginVarTracker::DeleteObjectInfoIfNecessary(VarMap::iterator iter) { 428 // Get the info before calling the base class's version of this function, 429 // which may delete the object. 430 ProxyObjectVar* object = iter->second.var->AsProxyObjectVar(); 431 HostVar host_var(object->dispatcher(), object->host_var_id()); 432 433 if (!VarTracker::DeleteObjectInfoIfNecessary(iter)) 434 return false; 435 436 // Clean up the host var mapping. 437 DCHECK(host_var_to_plugin_var_.find(host_var) != 438 host_var_to_plugin_var_.end()); 439 host_var_to_plugin_var_.erase(host_var); 440 return true; 441 } 442 443 PP_Var PluginVarTracker::GetOrCreateObjectVarID(ProxyObjectVar* object) { 444 // We can't use object->GetPPVar() because we don't want to affect the 445 // refcount, so we have to add everything manually here. 446 int32 var_id = object->GetExistingVarID(); 447 if (!var_id) { 448 var_id = AddVarInternal(object, ADD_VAR_CREATE_WITH_NO_REFERENCE); 449 object->AssignVarID(var_id); 450 } 451 452 PP_Var ret = { PP_VARTYPE_OBJECT }; 453 ret.value.as_id = var_id; 454 return ret; 455 } 456 457 void PluginVarTracker::SendAddRefObjectMsg( 458 const ProxyObjectVar& proxy_object) { 459 int unused; 460 if (proxy_object.dispatcher()) { 461 proxy_object.dispatcher()->Send(new PpapiHostMsg_PPBVar_AddRefObject( 462 API_ID_PPB_VAR_DEPRECATED, proxy_object.host_var_id(), &unused)); 463 } 464 } 465 466 void PluginVarTracker::SendReleaseObjectMsg( 467 const ProxyObjectVar& proxy_object) { 468 if (proxy_object.dispatcher()) { 469 proxy_object.dispatcher()->Send(new PpapiHostMsg_PPBVar_ReleaseObject( 470 API_ID_PPB_VAR_DEPRECATED, proxy_object.host_var_id())); 471 } 472 } 473 474 scoped_refptr<ProxyObjectVar> PluginVarTracker::FindOrMakePluginVarFromHostVar( 475 const PP_Var& var, 476 PluginDispatcher* dispatcher) { 477 DCHECK(var.type == PP_VARTYPE_OBJECT); 478 HostVar host_var(dispatcher, var.value.as_id); 479 480 HostVarToPluginVarMap::iterator found = 481 host_var_to_plugin_var_.find(host_var); 482 if (found == host_var_to_plugin_var_.end()) { 483 // Create a new object. 484 return scoped_refptr<ProxyObjectVar>( 485 new ProxyObjectVar(dispatcher, static_cast<int32>(var.value.as_id))); 486 } 487 488 // Have this host var, look up the object. 489 VarMap::iterator ret = live_vars_.find(found->second); 490 491 // We CHECK here because we currently don't fall back sanely. 492 // This may be involved in a NULL dereference. http://crbug.com/276347 493 CHECK(ret != live_vars_.end()); 494 495 // All objects should be proxy objects. 496 DCHECK(ret->second.var->AsProxyObjectVar()); 497 return scoped_refptr<ProxyObjectVar>(ret->second.var->AsProxyObjectVar()); 498 } 499 500 int PluginVarTracker::TrackSharedMemoryHandle(PP_Instance instance, 501 base::SharedMemoryHandle handle, 502 uint32 size_in_bytes) { 503 NOTREACHED(); 504 return -1; 505 } 506 507 bool PluginVarTracker::StopTrackingSharedMemoryHandle( 508 int id, 509 PP_Instance instance, 510 base::SharedMemoryHandle* handle, 511 uint32* size_in_bytes) { 512 NOTREACHED(); 513 return false; 514 } 515 516 } // namesace proxy 517 } // namespace ppapi 518