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