// Copyright (c) 2012 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_array_buffer_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 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 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 = { 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(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); } ArrayBufferVar* PluginVarTracker::CreateArrayBuffer(uint32 size_in_bytes) { return new PluginArrayBufferVar(size_in_bytes); } 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 = { 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 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( new ProxyObjectVar(dispatcher, static_cast(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(ret->second.var->AsProxyObjectVar()); } } // namesace proxy } // namespace ppapi