Fix bug in load time stats.
[chromium-blink-merge.git] / ppapi / proxy / plugin_var_tracker.cc
blob384eb3ec7b2165445c46daccba93c0fa731fe2d8
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.
5 #include "ppapi/proxy/plugin_var_tracker.h"
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/var.h"
18 namespace ppapi {
19 namespace proxy {
21 PluginVarTracker::HostVar::HostVar(PluginDispatcher* d, int32 i)
22 : dispatcher(d),
23 host_object_id(i) {
26 bool PluginVarTracker::HostVar::operator<(const HostVar& other) const {
27 if (dispatcher < other.dispatcher)
28 return true;
29 if (other.dispatcher < dispatcher)
30 return false;
31 return host_object_id < other.host_object_id;
34 PluginVarTracker::PluginVarTracker() {
37 PluginVarTracker::~PluginVarTracker() {
40 PP_Var PluginVarTracker::ReceiveObjectPassRef(const PP_Var& host_var,
41 PluginDispatcher* dispatcher) {
42 DCHECK(CalledOnValidThread());
43 DCHECK(host_var.type == PP_VARTYPE_OBJECT);
45 // Get the object.
46 scoped_refptr<ProxyObjectVar> object(
47 FindOrMakePluginVarFromHostVar(host_var, dispatcher));
49 // Actually create the PP_Var, this will add all the tracking info but not
50 // adjust any refcounts.
51 PP_Var ret = GetOrCreateObjectVarID(object.get());
53 VarInfo& info = GetLiveVar(ret)->second;
54 if (info.ref_count > 0) {
55 // We already had a reference to it before. That means the renderer now has
56 // two references on our behalf. We want to transfer that extra reference
57 // to our list. This means we addref in the plugin, and release the extra
58 // one in the renderer.
59 SendReleaseObjectMsg(*object);
61 info.ref_count++;
62 return ret;
65 PP_Var PluginVarTracker::TrackObjectWithNoReference(
66 const PP_Var& host_var,
67 PluginDispatcher* dispatcher) {
68 DCHECK(CalledOnValidThread());
69 DCHECK(host_var.type == PP_VARTYPE_OBJECT);
71 // Get the object.
72 scoped_refptr<ProxyObjectVar> object(
73 FindOrMakePluginVarFromHostVar(host_var, dispatcher));
75 // Actually create the PP_Var, this will add all the tracking info but not
76 // adjust any refcounts.
77 PP_Var ret = GetOrCreateObjectVarID(object.get());
79 VarInfo& info = GetLiveVar(ret)->second;
80 info.track_with_no_reference_count++;
81 return ret;
84 void PluginVarTracker::StopTrackingObjectWithNoReference(
85 const PP_Var& plugin_var) {
86 DCHECK(CalledOnValidThread());
87 DCHECK(plugin_var.type == PP_VARTYPE_OBJECT);
89 VarMap::iterator found = GetLiveVar(plugin_var);
90 if (found == live_vars_.end()) {
91 NOTREACHED();
92 return;
95 DCHECK(found->second.track_with_no_reference_count > 0);
96 found->second.track_with_no_reference_count--;
97 DeleteObjectInfoIfNecessary(found);
100 PP_Var PluginVarTracker::GetHostObject(const PP_Var& plugin_object) const {
101 DCHECK(CalledOnValidThread());
103 if (plugin_object.type != PP_VARTYPE_OBJECT) {
104 NOTREACHED();
105 return PP_MakeUndefined();
108 Var* var = GetVar(plugin_object);
109 ProxyObjectVar* object = var->AsProxyObjectVar();
110 if (!object) {
111 NOTREACHED();
112 return PP_MakeUndefined();
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;
121 PluginDispatcher* PluginVarTracker::DispatcherForPluginObject(
122 const PP_Var& plugin_object) const {
123 DCHECK(CalledOnValidThread());
125 if (plugin_object.type != PP_VARTYPE_OBJECT)
126 return NULL;
128 VarMap::const_iterator found = GetLiveVar(plugin_object);
129 if (found == live_vars_.end())
130 return NULL;
132 ProxyObjectVar* object = found->second.var->AsProxyObjectVar();
133 if (!object)
134 return NULL;
135 return object->dispatcher();
138 void PluginVarTracker::ReleaseHostObject(PluginDispatcher* dispatcher,
139 const PP_Var& host_object) {
140 DCHECK(CalledOnValidThread());
141 DCHECK(host_object.type == PP_VARTYPE_OBJECT);
143 // Convert the host object to a normal var valid in the plugin.
144 HostVarToPluginVarMap::iterator found = host_var_to_plugin_var_.find(
145 HostVar(dispatcher, static_cast<int32>(host_object.value.as_id)));
146 if (found == host_var_to_plugin_var_.end()) {
147 NOTREACHED();
148 return;
151 // Now just release the object given the plugin var ID.
152 ReleaseVar(found->second);
155 void PluginVarTracker::DidDeleteInstance(PP_Instance instance) {
156 // Calling the destructors on plugin objects may in turn release other
157 // objects which will mutate the map out from under us. So do a two-step
158 // process of identifying the ones to delete, and then delete them.
160 // See the comment above user_data_to_plugin_ in the header file. We assume
161 // there aren't that many objects so a brute-force search is reasonable.
162 std::vector<void*> user_data_to_delete;
163 for (UserDataToPluginImplementedVarMap::const_iterator i =
164 user_data_to_plugin_.begin();
165 i != user_data_to_plugin_.end();
166 ++i) {
167 if (i->second.instance == instance)
168 user_data_to_delete.push_back(i->first);
171 for (size_t i = 0; i < user_data_to_delete.size(); i++) {
172 UserDataToPluginImplementedVarMap::iterator found =
173 user_data_to_plugin_.find(user_data_to_delete[i]);
174 if (found == user_data_to_plugin_.end())
175 continue; // Object removed from list while we were iterating.
177 if (!found->second.plugin_object_id) {
178 // This object is for the freed instance and the plugin is not holding
179 // any references to it. Deallocate immediately.
180 found->second.ppp_class->Deallocate(found->first);
181 user_data_to_plugin_.erase(found);
182 } else {
183 // The plugin is holding refs to this object. We don't want to call
184 // Deallocate since the plugin may be depending on those refs to keep
185 // its data alive. To avoid crashes in this case, just clear out the
186 // instance to mark it and continue. When the plugin refs go to 0,
187 // we'll notice there is no instance and call Deallocate.
188 found->second.instance = 0;
193 ArrayBufferVar* PluginVarTracker::CreateArrayBuffer(uint32 size_in_bytes) {
194 return new PluginArrayBufferVar(size_in_bytes);
197 void PluginVarTracker::PluginImplementedObjectCreated(
198 PP_Instance instance,
199 const PP_Var& created_var,
200 const PPP_Class_Deprecated* ppp_class,
201 void* ppp_class_data) {
202 PluginImplementedVar p;
203 p.ppp_class = ppp_class;
204 p.instance = instance;
205 p.plugin_object_id = created_var.value.as_id;
206 user_data_to_plugin_[ppp_class_data] = p;
208 // Link the user data to the object.
209 ProxyObjectVar* object = GetVar(created_var)->AsProxyObjectVar();
210 object->set_user_data(ppp_class_data);
213 void PluginVarTracker::PluginImplementedObjectDestroyed(void* user_data) {
214 UserDataToPluginImplementedVarMap::iterator found =
215 user_data_to_plugin_.find(user_data);
216 if (found == user_data_to_plugin_.end()) {
217 NOTREACHED();
218 return;
220 user_data_to_plugin_.erase(found);
223 bool PluginVarTracker::IsPluginImplementedObjectAlive(void* user_data) {
224 return user_data_to_plugin_.find(user_data) != user_data_to_plugin_.end();
227 bool PluginVarTracker::ValidatePluginObjectCall(
228 const PPP_Class_Deprecated* ppp_class,
229 void* user_data) {
230 UserDataToPluginImplementedVarMap::iterator found =
231 user_data_to_plugin_.find(user_data);
232 if (found == user_data_to_plugin_.end())
233 return false;
234 return found->second.ppp_class == ppp_class;
237 int32 PluginVarTracker::AddVarInternal(Var* var, AddVarRefMode mode) {
238 // Normal adding.
239 int32 new_id = VarTracker::AddVarInternal(var, mode);
241 // Need to add proxy objects to the host var map.
242 ProxyObjectVar* proxy_object = var->AsProxyObjectVar();
243 if (proxy_object) {
244 HostVar host_var(proxy_object->dispatcher(), proxy_object->host_var_id());
245 DCHECK(host_var_to_plugin_var_.find(host_var) ==
246 host_var_to_plugin_var_.end()); // Adding an object twice, use
247 // FindOrMakePluginVarFromHostVar.
248 host_var_to_plugin_var_[host_var] = new_id;
250 return new_id;
253 void PluginVarTracker::TrackedObjectGettingOneRef(VarMap::const_iterator iter) {
254 ProxyObjectVar* object = iter->second.var->AsProxyObjectVar();
255 if (!object) {
256 NOTREACHED();
257 return;
260 DCHECK(iter->second.ref_count == 0);
262 // Got an AddRef for an object we have no existing reference for.
263 // We need to tell the browser we've taken a ref. This comes up when the
264 // browser passes an object as an input param and holds a ref for us.
265 // This must be a sync message since otherwise the "addref" will actually
266 // occur after the return to the browser of the sync function that
267 // presumably sent the object.
268 SendAddRefObjectMsg(*object);
271 void PluginVarTracker::ObjectGettingZeroRef(VarMap::iterator iter) {
272 ProxyObjectVar* object = iter->second.var->AsProxyObjectVar();
273 if (!object) {
274 NOTREACHED();
275 return;
278 // Notify the host we're no longer holding our ref.
279 DCHECK(iter->second.ref_count == 0);
280 SendReleaseObjectMsg(*object);
282 UserDataToPluginImplementedVarMap::iterator found =
283 user_data_to_plugin_.find(object->user_data());
284 if (found != user_data_to_plugin_.end()) {
285 // This object is implemented in the plugin.
286 if (found->second.instance == 0) {
287 // Instance is destroyed. This means that we'll never get a Deallocate
288 // call from the renderer and we should do so now.
289 found->second.ppp_class->Deallocate(found->first);
290 user_data_to_plugin_.erase(found);
291 } else {
292 // The plugin is releasing its last reference to an object it implements.
293 // Clear the tracking data that links our "plugin implemented object" to
294 // the var. If the instance is destroyed and there is no ID, we know that
295 // we should just call Deallocate on the object data.
297 // See the plugin_object_id declaration for more info.
298 found->second.plugin_object_id = 0;
302 // This will optionally delete the info from live_vars_.
303 VarTracker::ObjectGettingZeroRef(iter);
306 bool PluginVarTracker::DeleteObjectInfoIfNecessary(VarMap::iterator iter) {
307 // Get the info before calling the base class's version of this function,
308 // which may delete the object.
309 ProxyObjectVar* object = iter->second.var->AsProxyObjectVar();
310 HostVar host_var(object->dispatcher(), object->host_var_id());
312 if (!VarTracker::DeleteObjectInfoIfNecessary(iter))
313 return false;
315 // Clean up the host var mapping.
316 DCHECK(host_var_to_plugin_var_.find(host_var) !=
317 host_var_to_plugin_var_.end());
318 host_var_to_plugin_var_.erase(host_var);
319 return true;
322 PP_Var PluginVarTracker::GetOrCreateObjectVarID(ProxyObjectVar* object) {
323 // We can't use object->GetPPVar() because we don't want to affect the
324 // refcount, so we have to add everything manually here.
325 int32 var_id = object->GetExistingVarID();
326 if (!var_id) {
327 var_id = AddVarInternal(object, ADD_VAR_CREATE_WITH_NO_REFERENCE);
328 object->AssignVarID(var_id);
331 PP_Var ret = { PP_VARTYPE_OBJECT };
332 ret.value.as_id = var_id;
333 return ret;
336 void PluginVarTracker::SendAddRefObjectMsg(
337 const ProxyObjectVar& proxy_object) {
338 int unused;
339 proxy_object.dispatcher()->Send(new PpapiHostMsg_PPBVar_AddRefObject(
340 API_ID_PPB_VAR_DEPRECATED, proxy_object.host_var_id(), &unused));
343 void PluginVarTracker::SendReleaseObjectMsg(
344 const ProxyObjectVar& proxy_object) {
345 proxy_object.dispatcher()->Send(new PpapiHostMsg_PPBVar_ReleaseObject(
346 API_ID_PPB_VAR_DEPRECATED, proxy_object.host_var_id()));
349 scoped_refptr<ProxyObjectVar> PluginVarTracker::FindOrMakePluginVarFromHostVar(
350 const PP_Var& var,
351 PluginDispatcher* dispatcher) {
352 DCHECK(var.type == PP_VARTYPE_OBJECT);
353 HostVar host_var(dispatcher, var.value.as_id);
355 HostVarToPluginVarMap::iterator found =
356 host_var_to_plugin_var_.find(host_var);
357 if (found == host_var_to_plugin_var_.end()) {
358 // Create a new object.
359 return scoped_refptr<ProxyObjectVar>(
360 new ProxyObjectVar(dispatcher, static_cast<int32>(var.value.as_id)));
363 // Have this host var, look up the object.
364 VarMap::iterator ret = live_vars_.find(found->second);
365 DCHECK(ret != live_vars_.end());
367 // All objects should be proxy objects.
368 DCHECK(ret->second.var->AsProxyObjectVar());
369 return scoped_refptr<ProxyObjectVar>(ret->second.var->AsProxyObjectVar());
372 } // namesace proxy
373 } // namespace ppapi