blob: 8ea25b183cb20aed11a06dc1ebe175ad404c8bf0 [file] [log] [blame]
// 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