| // Copyright (c) 2011 The Chromium Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #include "ppapi/proxy/plugin_var_tracker.h" |
| |
| #include "base/memory/ref_counted.h" |
| #include "base/memory/singleton.h" |
| #include "ppapi/c/ppb_var.h" |
| #include "ppapi/proxy/plugin_dispatcher.h" |
| #include "ppapi/proxy/ppapi_messages.h" |
| #include "ppapi/proxy/proxy_object_var.h" |
| #include "ppapi/shared_impl/api_id.h" |
| #include "ppapi/shared_impl/var.h" |
| |
| namespace ppapi { |
| namespace proxy { |
| |
| PluginVarTracker::HostVar::HostVar(PluginDispatcher* d, int32 i) |
| : dispatcher(d), |
| host_object_id(i) { |
| } |
| |
| bool PluginVarTracker::HostVar::operator<(const HostVar& other) const { |
| if (dispatcher < other.dispatcher) |
| return true; |
| if (other.dispatcher < dispatcher) |
| return false; |
| return host_object_id < other.host_object_id; |
| } |
| |
| PluginVarTracker::PluginVarTracker() { |
| } |
| |
| PluginVarTracker::~PluginVarTracker() { |
| } |
| |
| PP_Var PluginVarTracker::ReceiveObjectPassRef(const PP_Var& host_var, |
| PluginDispatcher* dispatcher) { |
| DCHECK(host_var.type == PP_VARTYPE_OBJECT); |
| |
| // Get the object. |
| scoped_refptr<ProxyObjectVar> object( |
| FindOrMakePluginVarFromHostVar(host_var, dispatcher)); |
| |
| // Actually create the PP_Var, this will add all the tracking info but not |
| // adjust any refcounts. |
| PP_Var ret = GetOrCreateObjectVarID(object.get()); |
| |
| VarInfo& info = GetLiveVar(ret)->second; |
| if (info.ref_count > 0) { |
| // We already had a reference to it before. That means the renderer now has |
| // two references on our behalf. We want to transfer that extra reference |
| // to our list. This means we addref in the plugin, and release the extra |
| // one in the renderer. |
| SendReleaseObjectMsg(*object); |
| } |
| info.ref_count++; |
| return ret; |
| } |
| |
| PP_Var PluginVarTracker::TrackObjectWithNoReference( |
| const PP_Var& host_var, |
| PluginDispatcher* dispatcher) { |
| DCHECK(host_var.type == PP_VARTYPE_OBJECT); |
| |
| // Get the object. |
| scoped_refptr<ProxyObjectVar> object( |
| FindOrMakePluginVarFromHostVar(host_var, dispatcher)); |
| |
| // Actually create the PP_Var, this will add all the tracking info but not |
| // adjust any refcounts. |
| PP_Var ret = GetOrCreateObjectVarID(object.get()); |
| |
| VarInfo& info = GetLiveVar(ret)->second; |
| info.track_with_no_reference_count++; |
| return ret; |
| } |
| |
| void PluginVarTracker::StopTrackingObjectWithNoReference( |
| const PP_Var& plugin_var) { |
| DCHECK(plugin_var.type == PP_VARTYPE_OBJECT); |
| VarMap::iterator found = GetLiveVar(plugin_var); |
| if (found == live_vars_.end()) { |
| NOTREACHED(); |
| return; |
| } |
| |
| DCHECK(found->second.track_with_no_reference_count > 0); |
| found->second.track_with_no_reference_count--; |
| DeleteObjectInfoIfNecessary(found); |
| } |
| |
| PP_Var PluginVarTracker::GetHostObject(const PP_Var& plugin_object) const { |
| if (plugin_object.type != PP_VARTYPE_OBJECT) { |
| NOTREACHED(); |
| return PP_MakeUndefined(); |
| } |
| |
| Var* var = GetVar(plugin_object); |
| ProxyObjectVar* object = var->AsProxyObjectVar(); |
| if (!object) { |
| NOTREACHED(); |
| return PP_MakeUndefined(); |
| } |
| |
| // Make a var with the host ID. |
| PP_Var ret; |
| ret.type = PP_VARTYPE_OBJECT; |
| ret.value.as_id = object->host_var_id(); |
| return ret; |
| } |
| |
| PluginDispatcher* PluginVarTracker::DispatcherForPluginObject( |
| const PP_Var& plugin_object) const { |
| if (plugin_object.type != PP_VARTYPE_OBJECT) |
| return NULL; |
| |
| VarMap::const_iterator found = GetLiveVar(plugin_object); |
| if (found == live_vars_.end()) |
| return NULL; |
| |
| ProxyObjectVar* object = found->second.var->AsProxyObjectVar(); |
| if (!object) |
| return NULL; |
| return object->dispatcher(); |
| } |
| |
| void PluginVarTracker::ReleaseHostObject(PluginDispatcher* dispatcher, |
| const PP_Var& host_object) { |
| // Convert the host object to a normal var valid in the plugin. |
| DCHECK(host_object.type == PP_VARTYPE_OBJECT); |
| HostVarToPluginVarMap::iterator found = host_var_to_plugin_var_.find( |
| HostVar(dispatcher, static_cast<int32>(host_object.value.as_id))); |
| if (found == host_var_to_plugin_var_.end()) { |
| NOTREACHED(); |
| return; |
| } |
| |
| // Now just release the object given the plugin var ID. |
| ReleaseVar(found->second); |
| } |
| |
| int PluginVarTracker::GetRefCountForObject(const PP_Var& plugin_object) { |
| VarMap::iterator found = GetLiveVar(plugin_object); |
| if (found == live_vars_.end()) |
| return -1; |
| return found->second.ref_count; |
| } |
| |
| int PluginVarTracker::GetTrackedWithNoReferenceCountForObject( |
| const PP_Var& plugin_object) { |
| VarMap::iterator found = GetLiveVar(plugin_object); |
| if (found == live_vars_.end()) |
| return -1; |
| return found->second.track_with_no_reference_count; |
| } |
| |
| int32 PluginVarTracker::AddVarInternal(Var* var, AddVarRefMode mode) { |
| // Normal adding. |
| int32 new_id = VarTracker::AddVarInternal(var, mode); |
| |
| // Need to add proxy objects to the host var map. |
| ProxyObjectVar* proxy_object = var->AsProxyObjectVar(); |
| if (proxy_object) { |
| HostVar host_var(proxy_object->dispatcher(), proxy_object->host_var_id()); |
| DCHECK(host_var_to_plugin_var_.find(host_var) == |
| host_var_to_plugin_var_.end()); // Adding an object twice, use |
| // FindOrMakePluginVarFromHostVar. |
| host_var_to_plugin_var_[host_var] = new_id; |
| } |
| return new_id; |
| } |
| |
| void PluginVarTracker::TrackedObjectGettingOneRef(VarMap::const_iterator iter) { |
| ProxyObjectVar* object = iter->second.var->AsProxyObjectVar(); |
| if (!object) { |
| NOTREACHED(); |
| return; |
| } |
| |
| DCHECK(iter->second.ref_count == 0); |
| |
| // Got an AddRef for an object we have no existing reference for. |
| // We need to tell the browser we've taken a ref. This comes up when the |
| // browser passes an object as an input param and holds a ref for us. |
| // This must be a sync message since otherwise the "addref" will actually |
| // occur after the return to the browser of the sync function that |
| // presumably sent the object. |
| SendAddRefObjectMsg(*object); |
| } |
| |
| void PluginVarTracker::ObjectGettingZeroRef(VarMap::iterator iter) { |
| ProxyObjectVar* object = iter->second.var->AsProxyObjectVar(); |
| if (!object) { |
| NOTREACHED(); |
| return; |
| } |
| |
| // Notify the host we're no longer holding our ref. |
| DCHECK(iter->second.ref_count == 0); |
| SendReleaseObjectMsg(*object); |
| |
| // This will optionally delete the info from live_vars_. |
| VarTracker::ObjectGettingZeroRef(iter); |
| } |
| |
| bool PluginVarTracker::DeleteObjectInfoIfNecessary(VarMap::iterator iter) { |
| // Get the info before calling the base class's version of this function, |
| // which may delete the object. |
| ProxyObjectVar* object = iter->second.var->AsProxyObjectVar(); |
| HostVar host_var(object->dispatcher(), object->host_var_id()); |
| |
| if (!VarTracker::DeleteObjectInfoIfNecessary(iter)) |
| return false; |
| |
| // Clean up the host var mapping. |
| DCHECK(host_var_to_plugin_var_.find(host_var) != |
| host_var_to_plugin_var_.end()); |
| host_var_to_plugin_var_.erase(host_var); |
| return true; |
| } |
| |
| PP_Var PluginVarTracker::GetOrCreateObjectVarID(ProxyObjectVar* object) { |
| // We can't use object->GetPPVar() because we don't want to affect the |
| // refcount, so we have to add everything manually here. |
| int32 var_id = object->GetExistingVarID(); |
| if (!var_id) { |
| var_id = AddVarInternal(object, ADD_VAR_CREATE_WITH_NO_REFERENCE); |
| object->AssignVarID(var_id); |
| } |
| |
| PP_Var ret; |
| ret.type = PP_VARTYPE_OBJECT; |
| ret.value.as_id = var_id; |
| return ret; |
| } |
| |
| void PluginVarTracker::SendAddRefObjectMsg( |
| const ProxyObjectVar& proxy_object) { |
| int unused; |
| proxy_object.dispatcher()->Send(new PpapiHostMsg_PPBVar_AddRefObject( |
| API_ID_PPB_VAR_DEPRECATED, proxy_object.host_var_id(), &unused)); |
| } |
| |
| void PluginVarTracker::SendReleaseObjectMsg( |
| const ProxyObjectVar& proxy_object) { |
| proxy_object.dispatcher()->Send(new PpapiHostMsg_PPBVar_ReleaseObject( |
| API_ID_PPB_VAR_DEPRECATED, proxy_object.host_var_id())); |
| } |
| |
| scoped_refptr<ProxyObjectVar> PluginVarTracker::FindOrMakePluginVarFromHostVar( |
| const PP_Var& var, |
| PluginDispatcher* dispatcher) { |
| DCHECK(var.type == PP_VARTYPE_OBJECT); |
| HostVar host_var(dispatcher, var.value.as_id); |
| |
| HostVarToPluginVarMap::iterator found = |
| host_var_to_plugin_var_.find(host_var); |
| if (found == host_var_to_plugin_var_.end()) { |
| // Create a new object. |
| return scoped_refptr<ProxyObjectVar>( |
| new ProxyObjectVar(dispatcher, static_cast<int32>(var.value.as_id))); |
| } |
| |
| // Have this host var, look up the object. |
| VarMap::iterator ret = live_vars_.find(found->second); |
| DCHECK(ret != live_vars_.end()); |
| |
| // All objects should be proxy objects. |
| DCHECK(ret->second.var->AsProxyObjectVar()); |
| return scoped_refptr<ProxyObjectVar>(ret->second.var->AsProxyObjectVar()); |
| } |
| |
| } // namesace proxy |
| } // namespace ppapi |