| OLD | NEW |
| 1 // Copyright (c) 2011 The Chromium Authors. All rights reserved. | 1 // Copyright (c) 2011 The Chromium Authors. All rights reserved. |
| 2 // Use of this source code is governed by a BSD-style license that can be | 2 // Use of this source code is governed by a BSD-style license that can be |
| 3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
| 4 | 4 |
| 5 #include "webkit/plugins/ppapi/resource_tracker.h" | 5 #include "webkit/plugins/ppapi/resource_tracker.h" |
| 6 | 6 |
| 7 #include <limits> | 7 #include <limits> |
| 8 #include <set> | 8 #include <set> |
| 9 | 9 |
| 10 #include "base/logging.h" | 10 #include "base/logging.h" |
| (...skipping 32 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 43 | 43 |
| 44 typedef std::map<NPObject*, NPObjectVar*> NPObjectToNPObjectVarMap; | 44 typedef std::map<NPObject*, NPObjectVar*> NPObjectToNPObjectVarMap; |
| 45 | 45 |
| 46 struct ResourceTracker::InstanceData { | 46 struct ResourceTracker::InstanceData { |
| 47 InstanceData() : instance(0) {} | 47 InstanceData() : instance(0) {} |
| 48 | 48 |
| 49 // Non-owning pointer to the instance object. When a PluginInstance is | 49 // Non-owning pointer to the instance object. When a PluginInstance is |
| 50 // destroyed, it will notify us and we'll delete all associated data. | 50 // destroyed, it will notify us and we'll delete all associated data. |
| 51 PluginInstance* instance; | 51 PluginInstance* instance; |
| 52 | 52 |
| 53 // Resources associated with the instance. | |
| 54 ResourceSet ref_resources; | |
| 55 std::set<Resource*> assoc_resources; | |
| 56 | |
| 57 // Tracks all live NPObjectVars used by this module so we can map NPObjects | 53 // Tracks all live NPObjectVars used by this module so we can map NPObjects |
| 58 // to the corresponding object, and also release these properly if the | 54 // to the corresponding object, and also release these properly if the |
| 59 // instance goes away when there are still refs. These are non-owning | 55 // instance goes away when there are still refs. These are non-owning |
| 60 // references. | 56 // references. |
| 61 NPObjectToNPObjectVarMap np_object_to_object_var; | 57 NPObjectToNPObjectVarMap np_object_to_object_var; |
| 62 | 58 |
| 63 // Lazily allocated function proxies for the different interfaces. | 59 // Lazily allocated function proxies for the different interfaces. |
| 64 scoped_ptr< ::ppapi::FunctionGroupBase > | 60 scoped_ptr< ::ppapi::FunctionGroupBase > |
| 65 function_proxies[::pp::proxy::INTERFACE_ID_COUNT]; | 61 function_proxies[::pp::proxy::INTERFACE_ID_COUNT]; |
| 66 }; | 62 }; |
| 67 | 63 |
| 68 scoped_refptr<Resource> ResourceTracker::GetResource(PP_Resource res) const { | |
| 69 DLOG_IF(ERROR, !CheckIdType(res, ::ppapi::PP_ID_TYPE_RESOURCE)) | |
| 70 << res << " is not a PP_Resource."; | |
| 71 ResourceMap::const_iterator result = live_resources_.find(res); | |
| 72 if (result == live_resources_.end()) { | |
| 73 return scoped_refptr<Resource>(); | |
| 74 } | |
| 75 return result->second.first; | |
| 76 } | |
| 77 | |
| 78 // static | 64 // static |
| 79 ResourceTracker* ResourceTracker::global_tracker_ = NULL; | 65 ResourceTracker* ResourceTracker::global_tracker_ = NULL; |
| 80 ResourceTracker* ResourceTracker::singleton_override_ = NULL; | 66 ResourceTracker* ResourceTracker::singleton_override_ = NULL; |
| 81 | 67 |
| 82 ResourceTracker::ResourceTracker() | 68 ResourceTracker::ResourceTracker() |
| 83 : last_resource_id_(0) { | 69 : last_resource_id_(0) { |
| 84 // Wire up the new shared resource tracker base to use our implementation. | 70 // Wire up the new shared resource tracker base to use our implementation. |
| 85 ::ppapi::TrackerBase::Init(&GetTrackerBase); | 71 ::ppapi::TrackerBase::Init(&GetTrackerBase); |
| 86 } | 72 } |
| 87 | 73 |
| 88 ResourceTracker::~ResourceTracker() { | 74 ResourceTracker::~ResourceTracker() { |
| 89 } | 75 } |
| 90 | 76 |
| 91 // static | 77 // static |
| 92 ResourceTracker* ResourceTracker::Get() { | 78 ResourceTracker* ResourceTracker::Get() { |
| 93 if (singleton_override_) | 79 if (singleton_override_) |
| 94 return singleton_override_; | 80 return singleton_override_; |
| 95 if (!global_tracker_) | 81 if (!global_tracker_) |
| 96 global_tracker_ = new ResourceTracker; | 82 global_tracker_ = new ResourceTracker; |
| 97 return global_tracker_; | 83 return global_tracker_; |
| 98 } | 84 } |
| 99 | 85 |
| 100 void ResourceTracker::ResourceCreated(Resource* resource, | |
| 101 PluginInstance* instance) { | |
| 102 if (!instance) | |
| 103 return; | |
| 104 PP_Instance pp_instance = instance->pp_instance(); | |
| 105 DCHECK(pp_instance); | |
| 106 DCHECK(instance_map_.find(pp_instance) != instance_map_.end()); | |
| 107 instance_map_[pp_instance]->assoc_resources.insert(resource); | |
| 108 } | |
| 109 | |
| 110 void ResourceTracker::ResourceDestroyed(Resource* resource) { | |
| 111 if (!resource->instance()) | |
| 112 return; | |
| 113 | |
| 114 PP_Instance pp_instance = resource->instance()->pp_instance(); | |
| 115 DCHECK(pp_instance); | |
| 116 DCHECK(instance_map_.find(pp_instance) != instance_map_.end()); | |
| 117 | |
| 118 instance_map_[pp_instance]->assoc_resources.erase(resource); | |
| 119 } | |
| 120 | |
| 121 PP_Resource ResourceTracker::AddResource(Resource* resource) { | |
| 122 // If the plugin manages to create 1 billion resources, don't do crazy stuff. | |
| 123 if (last_resource_id_ == | |
| 124 (std::numeric_limits<PP_Resource>::max() >> ::ppapi::kPPIdTypeBits)) | |
| 125 return 0; | |
| 126 | |
| 127 // Add the resource with plugin use-count 1. | |
| 128 PP_Resource new_id = MakeTypedId(++last_resource_id_, | |
| 129 ::ppapi::PP_ID_TYPE_RESOURCE); | |
| 130 live_resources_.insert(std::make_pair(new_id, std::make_pair(resource, 1))); | |
| 131 | |
| 132 // Track associated with the instance. | |
| 133 PP_Instance pp_instance = resource->instance()->pp_instance(); | |
| 134 DCHECK(instance_map_.find(pp_instance) != instance_map_.end()); | |
| 135 instance_map_[pp_instance]->ref_resources.insert(new_id); | |
| 136 return new_id; | |
| 137 } | |
| 138 | |
| 139 bool ResourceTracker::AddRefResource(PP_Resource res) { | |
| 140 DLOG_IF(ERROR, !CheckIdType(res, ::ppapi::PP_ID_TYPE_RESOURCE)) | |
| 141 << res << " is not a PP_Resource."; | |
| 142 ResourceMap::iterator i = live_resources_.find(res); | |
| 143 if (i != live_resources_.end()) { | |
| 144 // We don't protect against overflow, since a plugin as malicious as to ref | |
| 145 // once per every byte in the address space could have just as well unrefed | |
| 146 // one time too many. | |
| 147 ++i->second.second; | |
| 148 return true; | |
| 149 } else { | |
| 150 return false; | |
| 151 } | |
| 152 } | |
| 153 | |
| 154 bool ResourceTracker::UnrefResource(PP_Resource res) { | |
| 155 DLOG_IF(ERROR, !CheckIdType(res, ::ppapi::PP_ID_TYPE_RESOURCE)) | |
| 156 << res << " is not a PP_Resource."; | |
| 157 ResourceMap::iterator i = live_resources_.find(res); | |
| 158 if (i != live_resources_.end()) { | |
| 159 if (!--i->second.second) { | |
| 160 Resource* to_release = i->second.first; | |
| 161 // LastPluginRefWasDeleted will clear the instance pointer, so save it | |
| 162 // first. | |
| 163 PP_Instance instance = to_release->instance()->pp_instance(); | |
| 164 to_release->LastPluginRefWasDeleted(); | |
| 165 | |
| 166 instance_map_[instance]->ref_resources.erase(res); | |
| 167 live_resources_.erase(i); | |
| 168 } | |
| 169 return true; | |
| 170 } else { | |
| 171 return false; | |
| 172 } | |
| 173 } | |
| 174 | |
| 175 void ResourceTracker::CleanupInstanceData(PP_Instance instance, | 86 void ResourceTracker::CleanupInstanceData(PP_Instance instance, |
| 176 bool delete_instance) { | 87 bool delete_instance) { |
| 177 DLOG_IF(ERROR, !CheckIdType(instance, ::ppapi::PP_ID_TYPE_INSTANCE)) | 88 DLOG_IF(ERROR, !CheckIdType(instance, ::ppapi::PP_ID_TYPE_INSTANCE)) |
| 178 << instance << " is not a PP_Instance."; | 89 << instance << " is not a PP_Instance."; |
| 179 InstanceMap::iterator found = instance_map_.find(instance); | 90 InstanceMap::iterator found = instance_map_.find(instance); |
| 180 if (found == instance_map_.end()) { | 91 if (found == instance_map_.end()) { |
| 181 NOTREACHED(); | 92 NOTREACHED(); |
| 182 return; | 93 return; |
| 183 } | 94 } |
| 184 InstanceData& data = *found->second; | 95 InstanceData& data = *found->second; |
| 185 | 96 |
| 186 // Force release all plugin references to resources associated with the | |
| 187 // deleted instance. | |
| 188 ResourceSet::iterator cur_res = data.ref_resources.begin(); | |
| 189 while (cur_res != data.ref_resources.end()) { | |
| 190 ResourceMap::iterator found_resource = live_resources_.find(*cur_res); | |
| 191 if (found_resource == live_resources_.end()) { | |
| 192 NOTREACHED(); | |
| 193 } else { | |
| 194 Resource* resource = found_resource->second.first; | |
| 195 | |
| 196 // Must delete from the resource set first since the resource's instance | |
| 197 // pointer will get zeroed out in LastPluginRefWasDeleted. | |
| 198 resource->LastPluginRefWasDeleted(); | |
| 199 live_resources_.erase(*cur_res); | |
| 200 } | |
| 201 | |
| 202 // Iterators to a set are stable so we can iterate the set while the items | |
| 203 // are being deleted as long as we're careful not to delete the item we're | |
| 204 // holding an iterator to. | |
| 205 ResourceSet::iterator current = cur_res++; | |
| 206 data.ref_resources.erase(current); | |
| 207 } | |
| 208 DCHECK(data.ref_resources.empty()); | |
| 209 | |
| 210 // Force delete all var references. Need to make a copy so we can iterate over | 97 // Force delete all var references. Need to make a copy so we can iterate over |
| 211 // the map while deleting stuff from it. | 98 // the map while deleting stuff from it. |
| 212 NPObjectToNPObjectVarMap np_object_map_copy = data.np_object_to_object_var; | 99 NPObjectToNPObjectVarMap np_object_map_copy = data.np_object_to_object_var; |
| 213 NPObjectToNPObjectVarMap::iterator cur_var = | 100 NPObjectToNPObjectVarMap::iterator cur_var = |
| 214 np_object_map_copy.begin(); | 101 np_object_map_copy.begin(); |
| 215 while (cur_var != np_object_map_copy.end()) { | 102 while (cur_var != np_object_map_copy.end()) { |
| 216 NPObjectToNPObjectVarMap::iterator current = cur_var++; | 103 NPObjectToNPObjectVarMap::iterator current = cur_var++; |
| 217 | 104 |
| 218 // Clear the object from the var mapping and the live instance object list. | 105 // Clear the object from the var mapping and the live instance object list. |
| 219 int32 var_id = current->second->GetExistingVarID(); | 106 int32 var_id = current->second->GetExistingVarID(); |
| 220 if (var_id) | 107 if (var_id) |
| 221 live_vars_.erase(var_id); | 108 live_vars_.erase(var_id); |
| 222 | 109 |
| 223 current->second->InstanceDeleted(); | 110 current->second->InstanceDeleted(); |
| 224 data.np_object_to_object_var.erase(current->first); | 111 data.np_object_to_object_var.erase(current->first); |
| 225 } | 112 } |
| 226 DCHECK(data.np_object_to_object_var.empty()); | 113 DCHECK(data.np_object_to_object_var.empty()); |
| 227 | 114 |
| 228 // Clear any resources that still reference this instance. | |
| 229 for (std::set<Resource*>::iterator res = data.assoc_resources.begin(); | |
| 230 res != data.assoc_resources.end(); | |
| 231 ++res) | |
| 232 (*res)->ClearInstance(); | |
| 233 data.assoc_resources.clear(); | |
| 234 | |
| 235 if (delete_instance) | 115 if (delete_instance) |
| 236 instance_map_.erase(found); | 116 instance_map_.erase(found); |
| 237 } | 117 } |
| 238 | 118 |
| 239 uint32 ResourceTracker::GetLiveObjectsForInstance( | |
| 240 PP_Instance instance) const { | |
| 241 InstanceMap::const_iterator found = instance_map_.find(instance); | |
| 242 if (found == instance_map_.end()) | |
| 243 return 0; | |
| 244 return static_cast<uint32>(found->second->ref_resources.size() + | |
| 245 found->second->np_object_to_object_var.size()); | |
| 246 } | |
| 247 | |
| 248 ::ppapi::ResourceObjectBase* ResourceTracker::GetResourceAPI( | |
| 249 PP_Resource res) { | |
| 250 DLOG_IF(ERROR, !CheckIdType(res, ::ppapi::PP_ID_TYPE_RESOURCE)) | |
| 251 << res << " is not a PP_Resource."; | |
| 252 ResourceMap::const_iterator result = live_resources_.find(res); | |
| 253 if (result == live_resources_.end()) | |
| 254 return NULL; | |
| 255 return result->second.first.get(); | |
| 256 } | |
| 257 | |
| 258 ::ppapi::FunctionGroupBase* ResourceTracker::GetFunctionAPI( | 119 ::ppapi::FunctionGroupBase* ResourceTracker::GetFunctionAPI( |
| 259 PP_Instance pp_instance, | 120 PP_Instance pp_instance, |
| 260 pp::proxy::InterfaceID id) { | 121 pp::proxy::InterfaceID id) { |
| 261 // Get the instance object. This also ensures that the instance data is in | 122 // Get the instance object. This also ensures that the instance data is in |
| 262 // the map, since we need it below. | 123 // the map, since we need it below. |
| 263 PluginInstance* instance = GetInstance(pp_instance); | 124 PluginInstance* instance = GetInstance(pp_instance); |
| 264 if (!instance) | 125 if (!instance) |
| 265 return NULL; | 126 return NULL; |
| 266 | 127 |
| 267 // The instance one is special, since it's just implemented by the instance | 128 // The instance one is special, since it's just implemented by the instance |
| (...skipping 22 matching lines...) Expand all Loading... |
| 290 case pp::proxy::INTERFACE_ID_RESOURCE_CREATION: | 151 case pp::proxy::INTERFACE_ID_RESOURCE_CREATION: |
| 291 proxy.reset(new ResourceCreationImpl(instance)); | 152 proxy.reset(new ResourceCreationImpl(instance)); |
| 292 break; | 153 break; |
| 293 default: | 154 default: |
| 294 NOTREACHED(); | 155 NOTREACHED(); |
| 295 } | 156 } |
| 296 | 157 |
| 297 return proxy.get(); | 158 return proxy.get(); |
| 298 } | 159 } |
| 299 | 160 |
| 300 PP_Instance ResourceTracker::GetInstanceForResource(PP_Resource pp_resource) { | |
| 301 scoped_refptr<Resource> resource(GetResource(pp_resource)); | |
| 302 if (!resource.get()) | |
| 303 return 0; | |
| 304 return resource->instance()->pp_instance(); | |
| 305 } | |
| 306 | |
| 307 ::ppapi::VarTracker* ResourceTracker::GetVarTracker() { | 161 ::ppapi::VarTracker* ResourceTracker::GetVarTracker() { |
| 308 return &var_tracker_; | 162 return &var_tracker_; |
| 309 } | 163 } |
| 310 | 164 |
| 165 ::ppapi::ResourceTracker* ResourceTracker::GetResourceTracker() { |
| 166 return this; |
| 167 } |
| 168 |
| 311 void ResourceTracker::AddNPObjectVar(NPObjectVar* object_var) { | 169 void ResourceTracker::AddNPObjectVar(NPObjectVar* object_var) { |
| 312 DCHECK(instance_map_.find(object_var->pp_instance()) != instance_map_.end()); | 170 DCHECK(instance_map_.find(object_var->pp_instance()) != instance_map_.end()); |
| 313 InstanceData& data = *instance_map_[object_var->pp_instance()].get(); | 171 InstanceData& data = *instance_map_[object_var->pp_instance()].get(); |
| 314 | 172 |
| 315 DCHECK(data.np_object_to_object_var.find(object_var->np_object()) == | 173 DCHECK(data.np_object_to_object_var.find(object_var->np_object()) == |
| 316 data.np_object_to_object_var.end()) << "NPObjectVar already in map"; | 174 data.np_object_to_object_var.end()) << "NPObjectVar already in map"; |
| 317 data.np_object_to_object_var[object_var->np_object()] = object_var; | 175 data.np_object_to_object_var[object_var->np_object()] = object_var; |
| 318 } | 176 } |
| 319 | 177 |
| 320 void ResourceTracker::RemoveNPObjectVar(NPObjectVar* object_var) { | 178 void ResourceTracker::RemoveNPObjectVar(NPObjectVar* object_var) { |
| (...skipping 18 matching lines...) Expand all Loading... |
| 339 DCHECK(instance_map_.find(instance) != instance_map_.end()); | 197 DCHECK(instance_map_.find(instance) != instance_map_.end()); |
| 340 InstanceData& data = *instance_map_[instance].get(); | 198 InstanceData& data = *instance_map_[instance].get(); |
| 341 | 199 |
| 342 NPObjectToNPObjectVarMap::iterator found = | 200 NPObjectToNPObjectVarMap::iterator found = |
| 343 data.np_object_to_object_var.find(np_object); | 201 data.np_object_to_object_var.find(np_object); |
| 344 if (found == data.np_object_to_object_var.end()) | 202 if (found == data.np_object_to_object_var.end()) |
| 345 return NULL; | 203 return NULL; |
| 346 return found->second; | 204 return found->second; |
| 347 } | 205 } |
| 348 | 206 |
| 207 int ResourceTracker::GetLiveNPObjectVarsForInstance( |
| 208 PP_Instance instance) const { |
| 209 InstanceMap::const_iterator found = instance_map_.find(instance); |
| 210 if (found == instance_map_.end()) |
| 211 return 0; |
| 212 return static_cast<int>(found->second->np_object_to_object_var.size()); |
| 213 } |
| 214 |
| 349 PP_Instance ResourceTracker::AddInstance(PluginInstance* instance) { | 215 PP_Instance ResourceTracker::AddInstance(PluginInstance* instance) { |
| 350 DCHECK(instance_map_.find(instance->pp_instance()) == instance_map_.end()); | 216 DCHECK(instance_map_.find(instance->pp_instance()) == instance_map_.end()); |
| 351 | 217 |
| 352 // Use a random number for the instance ID. This helps prevent some | 218 // Use a random number for the instance ID. This helps prevent some |
| 353 // accidents. See also AddModule below. | 219 // accidents. See also AddModule below. |
| 354 // | 220 // |
| 355 // Need to make sure the random number isn't a duplicate or 0. | 221 // Need to make sure the random number isn't a duplicate or 0. |
| 356 PP_Instance new_instance; | 222 PP_Instance new_instance; |
| 357 do { | 223 do { |
| 358 new_instance = MakeTypedId(static_cast<PP_Instance>(base::RandUint64()), | 224 new_instance = MakeTypedId(static_cast<PP_Instance>(base::RandUint64()), |
| 359 ::ppapi::PP_ID_TYPE_INSTANCE); | 225 ::ppapi::PP_ID_TYPE_INSTANCE); |
| 360 } while (!new_instance || | 226 } while (!new_instance || |
| 361 instance_map_.find(new_instance) != instance_map_.end() || | 227 instance_map_.find(new_instance) != instance_map_.end() || |
| 362 !instance->module()->ReserveInstanceID(new_instance)); | 228 !instance->module()->ReserveInstanceID(new_instance)); |
| 363 | 229 |
| 364 instance_map_[new_instance] = linked_ptr<InstanceData>(new InstanceData); | 230 instance_map_[new_instance] = linked_ptr<InstanceData>(new InstanceData); |
| 365 instance_map_[new_instance]->instance = instance; | 231 instance_map_[new_instance]->instance = instance; |
| 232 |
| 233 DidCreateInstance(new_instance); |
| 366 return new_instance; | 234 return new_instance; |
| 367 } | 235 } |
| 368 | 236 |
| 369 void ResourceTracker::InstanceDeleted(PP_Instance instance) { | 237 void ResourceTracker::InstanceDeleted(PP_Instance instance) { |
| 238 DidDeleteInstance(instance); |
| 370 CleanupInstanceData(instance, true); | 239 CleanupInstanceData(instance, true); |
| 371 } | 240 } |
| 372 | 241 |
| 373 void ResourceTracker::InstanceCrashed(PP_Instance instance) { | 242 void ResourceTracker::InstanceCrashed(PP_Instance instance) { |
| 243 DidDeleteInstance(instance); |
| 374 CleanupInstanceData(instance, false); | 244 CleanupInstanceData(instance, false); |
| 375 } | 245 } |
| 376 | 246 |
| 377 PluginInstance* ResourceTracker::GetInstance(PP_Instance instance) { | 247 PluginInstance* ResourceTracker::GetInstance(PP_Instance instance) { |
| 378 DLOG_IF(ERROR, !CheckIdType(instance, ::ppapi::PP_ID_TYPE_INSTANCE)) | 248 DLOG_IF(ERROR, !CheckIdType(instance, ::ppapi::PP_ID_TYPE_INSTANCE)) |
| 379 << instance << " is not a PP_Instance."; | 249 << instance << " is not a PP_Instance."; |
| 380 InstanceMap::iterator found = instance_map_.find(instance); | 250 InstanceMap::iterator found = instance_map_.find(instance); |
| 381 if (found == instance_map_.end()) | 251 if (found == instance_map_.end()) |
| 382 return NULL; | 252 return NULL; |
| 383 return found->second->instance; | 253 return found->second->instance; |
| (...skipping 46 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 430 | 300 |
| 431 // static | 301 // static |
| 432 void ResourceTracker::ClearSingletonOverride() { | 302 void ResourceTracker::ClearSingletonOverride() { |
| 433 DCHECK(singleton_override_); | 303 DCHECK(singleton_override_); |
| 434 singleton_override_ = NULL; | 304 singleton_override_ = NULL; |
| 435 } | 305 } |
| 436 | 306 |
| 437 } // namespace ppapi | 307 } // namespace ppapi |
| 438 } // namespace webkit | 308 } // namespace webkit |
| 439 | 309 |
| OLD | NEW |