OLD | NEW |
---|---|
1 // Copyright (c) 2010 The Chromium Authors. All rights reserved. | 1 // Copyright (c) 2010 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/lazy_instance.h" | 10 #include "base/lazy_instance.h" |
11 #include "base/logging.h" | 11 #include "base/logging.h" |
12 #include "base/rand_util.h" | 12 #include "base/rand_util.h" |
13 #include "ppapi/c/pp_resource.h" | 13 #include "ppapi/c/pp_resource.h" |
14 #include "webkit/plugins/ppapi/ppapi_plugin_instance.h" | 14 #include "webkit/plugins/ppapi/ppapi_plugin_instance.h" |
15 #include "webkit/plugins/ppapi/resource.h" | 15 #include "webkit/plugins/ppapi/resource.h" |
16 #include "webkit/plugins/ppapi/var.h" | 16 #include "webkit/plugins/ppapi/var.h" |
17 | 17 |
18 enum PPIdType { | |
19 PP_ID_TYPE_MODULE, | |
20 PP_ID_TYPE_INSTANCE, | |
21 PP_ID_TYPE_RESOURCE, | |
22 PP_ID_TYPE_VAR, | |
23 PP_ID_TYPE_COUNT | |
24 }; | |
25 | |
26 static const unsigned int kPPIdTypeBits = 2; | |
27 COMPILE_ASSERT(PP_ID_TYPE_COUNT <= (1<<kPPIdTypeBits), | |
28 kPPIdTypeBits_is_too_small_for_all_id_types); | |
29 | |
30 template <typename T> static inline T MakeTypedId(T value, PPIdType type) { | |
31 return (value << kPPIdTypeBits) | static_cast<T>(type); | |
32 } | |
33 | |
34 template <typename T> static inline bool CheckIdType(T id, PPIdType type) { | |
35 // 0 is a valid resource. | |
36 if (!id) | |
37 return true; | |
38 const T mask = (static_cast<T>(1) << kPPIdTypeBits) - 1; | |
39 return (id & mask) == type; | |
40 } | |
41 | |
18 namespace webkit { | 42 namespace webkit { |
19 namespace ppapi { | 43 namespace ppapi { |
20 | 44 |
21 static base::LazyInstance<ResourceTracker> g_resource_tracker( | 45 static base::LazyInstance<ResourceTracker> g_resource_tracker( |
22 base::LINKER_INITIALIZED); | 46 base::LINKER_INITIALIZED); |
23 | 47 |
24 scoped_refptr<Resource> ResourceTracker::GetResource(PP_Resource res) const { | 48 scoped_refptr<Resource> ResourceTracker::GetResource(PP_Resource res) const { |
49 DLOG_IF(ERROR, !CheckIdType(res, PP_ID_TYPE_RESOURCE)) | |
50 << res << " is not a PP_Resource."; | |
25 ResourceMap::const_iterator result = live_resources_.find(res); | 51 ResourceMap::const_iterator result = live_resources_.find(res); |
26 if (result == live_resources_.end()) { | 52 if (result == live_resources_.end()) { |
27 return scoped_refptr<Resource>(); | 53 return scoped_refptr<Resource>(); |
28 } | 54 } |
29 return result->second.first; | 55 return result->second.first; |
30 } | 56 } |
31 | 57 |
32 // static | 58 // static |
33 ResourceTracker* ResourceTracker::singleton_override_ = NULL; | 59 ResourceTracker* ResourceTracker::singleton_override_ = NULL; |
34 | 60 |
35 ResourceTracker::ResourceTracker() | 61 ResourceTracker::ResourceTracker() |
36 : last_resource_id_(0), | 62 : last_resource_id_(0), |
37 last_var_id_(0) { | 63 last_var_id_(0) { |
38 } | 64 } |
39 | 65 |
40 ResourceTracker::~ResourceTracker() { | 66 ResourceTracker::~ResourceTracker() { |
41 } | 67 } |
42 | 68 |
43 // static | 69 // static |
44 ResourceTracker* ResourceTracker::Get() { | 70 ResourceTracker* ResourceTracker::Get() { |
45 if (singleton_override_) | 71 if (singleton_override_) |
46 return singleton_override_; | 72 return singleton_override_; |
47 return g_resource_tracker.Pointer(); | 73 return g_resource_tracker.Pointer(); |
48 } | 74 } |
49 | 75 |
50 PP_Resource ResourceTracker::AddResource(Resource* resource) { | 76 PP_Resource ResourceTracker::AddResource(Resource* resource) { |
51 // If the plugin manages to create 4 billion resources, don't do crazy stuff. | 77 // If the plugin manages to create 1 billion resources, don't do crazy stuff. |
52 if (last_resource_id_ == std::numeric_limits<PP_Resource>::max()) | 78 if (last_resource_id_ == |
79 std::numeric_limits<PP_Resource>::max() >> kPPIdTypeBits) | |
brettw
2011/01/20 05:45:09
Can you add parens here to clarify the order of op
| |
53 return 0; | 80 return 0; |
54 | 81 |
55 // Add the resource with plugin use-count 1. | 82 // Add the resource with plugin use-count 1. |
56 PP_Resource new_id = ++last_resource_id_; | 83 PP_Resource new_id = MakeTypedId(++last_resource_id_, PP_ID_TYPE_RESOURCE); |
57 live_resources_.insert(std::make_pair(new_id, std::make_pair(resource, 1))); | 84 live_resources_.insert(std::make_pair(new_id, std::make_pair(resource, 1))); |
58 instance_to_resources_[resource->instance()->pp_instance()].insert(new_id); | 85 instance_to_resources_[resource->instance()->pp_instance()].insert(new_id); |
59 return new_id; | 86 return new_id; |
60 } | 87 } |
61 | 88 |
62 int32 ResourceTracker::AddVar(Var* var) { | 89 int32 ResourceTracker::AddVar(Var* var) { |
63 // If the plugin manages to create 4B strings... | 90 // If the plugin manages to create 1B strings... |
64 if (last_var_id_ == std::numeric_limits<int32>::max()) { | 91 if (last_var_id_ == std::numeric_limits<int32>::max() >> kPPIdTypeBits) { |
65 return 0; | 92 return 0; |
66 } | 93 } |
67 // Add the resource with plugin use-count 1. | 94 // Add the resource with plugin use-count 1. |
68 ++last_var_id_; | 95 int32 new_id = MakeTypedId(++last_var_id_, PP_ID_TYPE_VAR); |
69 live_vars_.insert(std::make_pair(last_var_id_, | 96 live_vars_.insert(std::make_pair(new_id, std::make_pair(var, 1))); |
70 std::make_pair(var, 1))); | 97 return new_id; |
71 return last_var_id_; | |
72 } | 98 } |
73 | 99 |
74 bool ResourceTracker::AddRefResource(PP_Resource res) { | 100 bool ResourceTracker::AddRefResource(PP_Resource res) { |
101 DLOG_IF(ERROR, !CheckIdType(res, PP_ID_TYPE_RESOURCE)) | |
102 << res << " is not a PP_Resource."; | |
75 ResourceMap::iterator i = live_resources_.find(res); | 103 ResourceMap::iterator i = live_resources_.find(res); |
76 if (i != live_resources_.end()) { | 104 if (i != live_resources_.end()) { |
77 // We don't protect against overflow, since a plugin as malicious as to ref | 105 // We don't protect against overflow, since a plugin as malicious as to ref |
78 // once per every byte in the address space could have just as well unrefed | 106 // once per every byte in the address space could have just as well unrefed |
79 // one time too many. | 107 // one time too many. |
80 ++i->second.second; | 108 ++i->second.second; |
81 return true; | 109 return true; |
82 } else { | 110 } else { |
83 return false; | 111 return false; |
84 } | 112 } |
85 } | 113 } |
86 | 114 |
87 bool ResourceTracker::UnrefResource(PP_Resource res) { | 115 bool ResourceTracker::UnrefResource(PP_Resource res) { |
116 DLOG_IF(ERROR, !CheckIdType(res, PP_ID_TYPE_RESOURCE)) | |
117 << res << " is not a PP_Resource."; | |
88 ResourceMap::iterator i = live_resources_.find(res); | 118 ResourceMap::iterator i = live_resources_.find(res); |
89 if (i != live_resources_.end()) { | 119 if (i != live_resources_.end()) { |
90 if (!--i->second.second) { | 120 if (!--i->second.second) { |
91 Resource* to_release = i->second.first; | 121 Resource* to_release = i->second.first; |
92 to_release->LastPluginRefWasDeleted(false); | 122 to_release->LastPluginRefWasDeleted(false); |
93 | 123 |
94 ResourceSet& instance_resource_set = | 124 ResourceSet& instance_resource_set = |
95 instance_to_resources_[to_release->instance()->pp_instance()]; | 125 instance_to_resources_[to_release->instance()->pp_instance()]; |
96 DCHECK(instance_resource_set.find(res) != instance_resource_set.end()); | 126 DCHECK(instance_resource_set.find(res) != instance_resource_set.end()); |
97 instance_resource_set.erase(res); | 127 instance_resource_set.erase(res); |
98 | 128 |
99 live_resources_.erase(i); | 129 live_resources_.erase(i); |
100 } | 130 } |
101 return true; | 131 return true; |
102 } else { | 132 } else { |
103 return false; | 133 return false; |
104 } | 134 } |
105 } | 135 } |
106 | 136 |
107 void ResourceTracker::ForceDeletePluginResourceRefs(PP_Resource res) { | 137 void ResourceTracker::ForceDeletePluginResourceRefs(PP_Resource res) { |
138 DLOG_IF(ERROR, !CheckIdType(res, PP_ID_TYPE_RESOURCE)) | |
139 << res << " is not a PP_Resource."; | |
108 ResourceMap::iterator i = live_resources_.find(res); | 140 ResourceMap::iterator i = live_resources_.find(res); |
109 if (i == live_resources_.end()) | 141 if (i == live_resources_.end()) |
110 return; // Nothing to do. | 142 return; // Nothing to do. |
111 | 143 |
112 i->second.second = 0; | 144 i->second.second = 0; |
113 Resource* resource = i->second.first; | 145 Resource* resource = i->second.first; |
114 | 146 |
115 // Must delete from the resource set first since the resource's instance | 147 // Must delete from the resource set first since the resource's instance |
116 // pointer will get zeroed out in LastPluginRefWasDeleted. | 148 // pointer will get zeroed out in LastPluginRefWasDeleted. |
117 ResourceSet& resource_set = instance_to_resources_[ | 149 ResourceSet& resource_set = instance_to_resources_[ |
(...skipping 13 matching lines...) Expand all Loading... | |
131 // module->resource lookup to free resources when a module is unloaded. In | 163 // module->resource lookup to free resources when a module is unloaded. In |
132 // this case, this function can be implemented using that system. | 164 // this case, this function can be implemented using that system. |
133 uint32 count = 0; | 165 uint32 count = 0; |
134 for (ResourceMap::const_iterator i = live_resources_.begin(); | 166 for (ResourceMap::const_iterator i = live_resources_.begin(); |
135 i != live_resources_.end(); ++i) | 167 i != live_resources_.end(); ++i) |
136 count++; | 168 count++; |
137 return count; | 169 return count; |
138 } | 170 } |
139 | 171 |
140 scoped_refptr<Var> ResourceTracker::GetVar(int32 var_id) const { | 172 scoped_refptr<Var> ResourceTracker::GetVar(int32 var_id) const { |
173 DLOG_IF(ERROR, !CheckIdType(var_id, PP_ID_TYPE_VAR)) | |
174 << var_id << " is not a PP_Var ID."; | |
141 VarMap::const_iterator result = live_vars_.find(var_id); | 175 VarMap::const_iterator result = live_vars_.find(var_id); |
142 if (result == live_vars_.end()) { | 176 if (result == live_vars_.end()) { |
143 return scoped_refptr<Var>(); | 177 return scoped_refptr<Var>(); |
144 } | 178 } |
145 return result->second.first; | 179 return result->second.first; |
146 } | 180 } |
147 | 181 |
148 bool ResourceTracker::AddRefVar(int32 var_id) { | 182 bool ResourceTracker::AddRefVar(int32 var_id) { |
183 DLOG_IF(ERROR, !CheckIdType(var_id, PP_ID_TYPE_VAR)) | |
184 << var_id << " is not a PP_Var ID."; | |
149 VarMap::iterator i = live_vars_.find(var_id); | 185 VarMap::iterator i = live_vars_.find(var_id); |
150 if (i != live_vars_.end()) { | 186 if (i != live_vars_.end()) { |
151 // We don't protect against overflow, since a plugin as malicious as to ref | 187 // We don't protect against overflow, since a plugin as malicious as to ref |
152 // once per every byte in the address space could have just as well unrefed | 188 // once per every byte in the address space could have just as well unrefed |
153 // one time too many. | 189 // one time too many. |
154 ++i->second.second; | 190 ++i->second.second; |
155 return true; | 191 return true; |
156 } | 192 } |
157 return false; | 193 return false; |
158 } | 194 } |
159 | 195 |
160 bool ResourceTracker::UnrefVar(int32 var_id) { | 196 bool ResourceTracker::UnrefVar(int32 var_id) { |
197 DLOG_IF(ERROR, !CheckIdType(var_id, PP_ID_TYPE_VAR)) | |
198 << var_id << " is not a PP_Var ID."; | |
161 VarMap::iterator i = live_vars_.find(var_id); | 199 VarMap::iterator i = live_vars_.find(var_id); |
162 if (i != live_vars_.end()) { | 200 if (i != live_vars_.end()) { |
163 if (!--i->second.second) | 201 if (!--i->second.second) |
164 live_vars_.erase(i); | 202 live_vars_.erase(i); |
165 return true; | 203 return true; |
166 } | 204 } |
167 return false; | 205 return false; |
168 } | 206 } |
169 | 207 |
170 PP_Instance ResourceTracker::AddInstance(PluginInstance* instance) { | 208 PP_Instance ResourceTracker::AddInstance(PluginInstance* instance) { |
171 #ifndef NDEBUG | 209 #ifndef NDEBUG |
172 // Make sure we're not adding one more than once. | 210 // Make sure we're not adding one more than once. |
173 for (InstanceMap::const_iterator i = instance_map_.begin(); | 211 for (InstanceMap::const_iterator i = instance_map_.begin(); |
174 i != instance_map_.end(); ++i) | 212 i != instance_map_.end(); ++i) |
175 DCHECK(i->second != instance); | 213 DCHECK(i->second != instance); |
176 #endif | 214 #endif |
177 | 215 |
178 // Use a random 64-bit number for the instance ID. This helps prevent some | 216 // Use a random 64-bit number for the instance ID. This helps prevent some |
179 // mischeif where you could misallocate resources if you gave a different | 217 // mischeif where you could misallocate resources if you gave a different |
180 // instance ID. | 218 // instance ID. |
181 // | 219 // |
182 // See also AddModule below. | 220 // See also AddModule below. |
183 // | 221 // |
184 // Need to make sure the random number isn't a duplicate or 0. | 222 // Need to make sure the random number isn't a duplicate or 0. |
185 PP_Instance new_instance; | 223 PP_Instance new_instance; |
186 do { | 224 do { |
187 new_instance = static_cast<PP_Instance>(base::RandUint64()); | 225 new_instance = MakeTypedId(static_cast<PP_Instance>(base::RandUint64()), |
226 PP_ID_TYPE_INSTANCE); | |
188 } while (!new_instance || | 227 } while (!new_instance || |
189 instance_map_.find(new_instance) != instance_map_.end()); | 228 instance_map_.find(new_instance) != instance_map_.end()); |
190 instance_map_[new_instance] = instance; | 229 instance_map_[new_instance] = instance; |
191 return new_instance; | 230 return new_instance; |
192 } | 231 } |
193 | 232 |
194 void ResourceTracker::InstanceDeleted(PP_Instance instance) { | 233 void ResourceTracker::InstanceDeleted(PP_Instance instance) { |
234 DLOG_IF(ERROR, !CheckIdType(instance, PP_ID_TYPE_INSTANCE)) | |
235 << instance << " is not a PP_Instance."; | |
195 // Force release all plugin references to resources associated with the | 236 // Force release all plugin references to resources associated with the |
196 // deleted instance. | 237 // deleted instance. |
197 ResourceSet& resource_set = instance_to_resources_[instance]; | 238 ResourceSet& resource_set = instance_to_resources_[instance]; |
198 ResourceSet::iterator i = resource_set.begin(); | 239 ResourceSet::iterator i = resource_set.begin(); |
199 while (i != resource_set.end()) { | 240 while (i != resource_set.end()) { |
200 // Iterators to a set are stable so we can iterate the set while the items | 241 // Iterators to a set are stable so we can iterate the set while the items |
201 // are being deleted as long as we're careful not to delete the item we're | 242 // are being deleted as long as we're careful not to delete the item we're |
202 // holding an iterator to. | 243 // holding an iterator to. |
203 ResourceSet::iterator current = i++; | 244 ResourceSet::iterator current = i++; |
204 ForceDeletePluginResourceRefs(*current); | 245 ForceDeletePluginResourceRefs(*current); |
205 } | 246 } |
206 DCHECK(resource_set.empty()); | 247 DCHECK(resource_set.empty()); |
207 instance_to_resources_.erase(instance); | 248 instance_to_resources_.erase(instance); |
208 | 249 |
209 InstanceMap::iterator found = instance_map_.find(instance); | 250 InstanceMap::iterator found = instance_map_.find(instance); |
210 if (found == instance_map_.end()) { | 251 if (found == instance_map_.end()) { |
211 NOTREACHED(); | 252 NOTREACHED(); |
212 return; | 253 return; |
213 } | 254 } |
214 instance_map_.erase(found); | 255 instance_map_.erase(found); |
215 } | 256 } |
216 | 257 |
217 PluginInstance* ResourceTracker::GetInstance(PP_Instance instance) { | 258 PluginInstance* ResourceTracker::GetInstance(PP_Instance instance) { |
259 DLOG_IF(ERROR, !CheckIdType(instance, PP_ID_TYPE_INSTANCE)) | |
260 << instance << " is not a PP_Instance."; | |
218 InstanceMap::iterator found = instance_map_.find(instance); | 261 InstanceMap::iterator found = instance_map_.find(instance); |
219 if (found == instance_map_.end()) | 262 if (found == instance_map_.end()) |
220 return NULL; | 263 return NULL; |
221 return found->second; | 264 return found->second; |
222 } | 265 } |
223 | 266 |
224 PP_Module ResourceTracker::AddModule(PluginModule* module) { | 267 PP_Module ResourceTracker::AddModule(PluginModule* module) { |
225 #ifndef NDEBUG | 268 #ifndef NDEBUG |
226 // Make sure we're not adding one more than once. | 269 // Make sure we're not adding one more than once. |
227 for (ModuleMap::const_iterator i = module_map_.begin(); | 270 for (ModuleMap::const_iterator i = module_map_.begin(); |
228 i != module_map_.end(); ++i) | 271 i != module_map_.end(); ++i) |
229 DCHECK(i->second != module); | 272 DCHECK(i->second != module); |
230 #endif | 273 #endif |
231 | 274 |
232 // See AddInstance above. | 275 // See AddInstance above. |
233 PP_Module new_module; | 276 PP_Module new_module; |
234 do { | 277 do { |
235 new_module = static_cast<PP_Module>(base::RandUint64()); | 278 new_module = MakeTypedId(static_cast<PP_Module>(base::RandUint64()), |
279 PP_ID_TYPE_MODULE); | |
236 } while (!new_module || | 280 } while (!new_module || |
237 module_map_.find(new_module) != module_map_.end()); | 281 module_map_.find(new_module) != module_map_.end()); |
238 module_map_[new_module] = module; | 282 module_map_[new_module] = module; |
239 return new_module; | 283 return new_module; |
240 } | 284 } |
241 | 285 |
242 void ResourceTracker::ModuleDeleted(PP_Module module) { | 286 void ResourceTracker::ModuleDeleted(PP_Module module) { |
287 DLOG_IF(ERROR, !CheckIdType(module, PP_ID_TYPE_MODULE)) | |
288 << module << " is not a PP_Module."; | |
243 ModuleMap::iterator found = module_map_.find(module); | 289 ModuleMap::iterator found = module_map_.find(module); |
244 if (found == module_map_.end()) { | 290 if (found == module_map_.end()) { |
245 NOTREACHED(); | 291 NOTREACHED(); |
246 return; | 292 return; |
247 } | 293 } |
248 module_map_.erase(found); | 294 module_map_.erase(found); |
249 } | 295 } |
250 | 296 |
251 PluginModule* ResourceTracker::GetModule(PP_Module module) { | 297 PluginModule* ResourceTracker::GetModule(PP_Module module) { |
298 DLOG_IF(ERROR, !CheckIdType(module, PP_ID_TYPE_MODULE)) | |
299 << module << " is not a PP_Module."; | |
252 ModuleMap::iterator found = module_map_.find(module); | 300 ModuleMap::iterator found = module_map_.find(module); |
253 if (found == module_map_.end()) | 301 if (found == module_map_.end()) |
254 return NULL; | 302 return NULL; |
255 return found->second; | 303 return found->second; |
256 } | 304 } |
257 | 305 |
258 // static | 306 // static |
259 void ResourceTracker::SetSingletonOverride(ResourceTracker* tracker) { | 307 void ResourceTracker::SetSingletonOverride(ResourceTracker* tracker) { |
260 DCHECK(!singleton_override_); | 308 DCHECK(!singleton_override_); |
261 singleton_override_ = tracker; | 309 singleton_override_ = tracker; |
262 } | 310 } |
263 | 311 |
264 // static | 312 // static |
265 void ResourceTracker::ClearSingletonOverride() { | 313 void ResourceTracker::ClearSingletonOverride() { |
266 DCHECK(singleton_override_); | 314 DCHECK(singleton_override_); |
267 singleton_override_ = NULL; | 315 singleton_override_ = NULL; |
268 } | 316 } |
269 | 317 |
270 } // namespace ppapi | 318 } // namespace ppapi |
271 } // namespace webkit | 319 } // namespace webkit |
272 | 320 |
OLD | NEW |