// 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.
#ifndef PPAPI_PROXY_ENTER_PROXY_H_
#define PPAPI_PROXY_ENTER_PROXY_H_
#include "base/logging.h"
#include "ppapi/cpp/completion_callback.h"
#include "ppapi/proxy/host_dispatcher.h"
#include "ppapi/proxy/plugin_dispatcher.h"
#include "ppapi/proxy/plugin_globals.h"
#include "ppapi/proxy/plugin_resource_tracker.h"
#include "ppapi/thunk/enter.h"
namespace ppapi {
namespace proxy {
// Wrapper around EnterResourceNoLock that takes a host resource. This is used
// when handling messages in the plugin from the host and we need to convert to
// an object in the plugin side corresponding to that.
//
// This never locks since we assume the host Resource is coming from IPC, and
// never logs errors since we assume the host is doing reasonable things.
template<typename ResourceT>
class EnterPluginFromHostResource
: public thunk::EnterResourceNoLock<ResourceT> {
public:
explicit EnterPluginFromHostResource(const HostResource& host_resource)
: thunk::EnterResourceNoLock<ResourceT>(
PluginGlobals::Get()->plugin_resource_tracker()->
PluginResourceForHostResource(host_resource),
false) {
// Validate that we're in the plugin rather than the host. Otherwise this
// object will do the wrong thing. In the plugin, the instance should have
// a corresponding plugin dispatcher (assuming the resource is valid).
DCHECK(this->failed() ||
PluginDispatcher::GetForInstance(host_resource.instance()));
}
};
template<typename ResourceT>
class EnterHostFromHostResource
: public thunk::EnterResourceNoLock<ResourceT> {
public:
explicit EnterHostFromHostResource(const HostResource& host_resource)
: thunk::EnterResourceNoLock<ResourceT>(host_resource.host_resource(),
false) {
// Validate that we're in the host rather than the plugin. Otherwise this
// object will do the wrong thing. In the host, the instance should have
// a corresponding host disptacher (assuming the resource is valid).
DCHECK(this->failed() ||
HostDispatcher::GetForInstance(host_resource.instance()));
}
EnterHostFromHostResource(const HostResource& host_resource,
const pp::CompletionCallback& callback)
: thunk::EnterResourceNoLock<ResourceT>(host_resource.host_resource(),
callback.pp_completion_callback(),
false) {
// Validate that we're in the host rather than the plugin. Otherwise this
// object will do the wrong thing. In the host, the instance should have
// a corresponding host disptacher (assuming the resource is valid).
DCHECK(this->failed() ||
HostDispatcher::GetForInstance(host_resource.instance()));
}
};
// Enters a resource and forces a completion callback to be issued.
//
// This is used when implementing the host (renderer) side of a resource
// function that issues a completion callback. In all cases, we need to issue
// the callback to avoid hanging the plugin.
//
// This class automatically constructs a callback with the given factory
// calling the given method. The method will generally be the one that sends
// the message to trigger the completion callback in the plugin process.
//
// It will automatically issue the callback with PP_ERROR_NOINTERFACE if the
// host resource is invalid (i.e. failed() is set). In all other cases you
// should call SetResult(), which will issue the callback immediately if the
// result value isn't PP_OK_COMPLETIONPENDING. In the "completion pending"
// case, it's assumed the function the proxy is calling will take responsibility
// of executing the callback (returned by callback()).
//
// Example:
// EnterHostFromHostResourceForceCallback<PPB_Foo_API> enter(
// resource, callback_factory_, &MyClass::SendResult, resource);
// if (enter.failed())
// return; // SendResult automatically called with PP_ERROR_BADRESOURCE.
// enter.SetResult(enter.object()->DoFoo(enter.callback()));
//
// Where DoFoo's signature looks like this:
// int32_t DoFoo(PP_CompletionCallback callback);
// And SendResult's implementation looks like this:
// void MyClass::SendResult(int32_t result, const HostResource& res) {
// Send(new FooMsg_FooComplete(..., result, res));
// }
template<typename ResourceT>
class EnterHostFromHostResourceForceCallback
: public EnterHostFromHostResource<ResourceT> {
public:
EnterHostFromHostResourceForceCallback(
const HostResource& host_resource,
const pp::CompletionCallback& callback)
: EnterHostFromHostResource<ResourceT>(host_resource, callback),
needs_running_(true) {
}
// For callbacks that take no parameters except the "int32_t result". Most
// implementations will use the 1-extra-argument constructor below.
template<class CallbackFactory, typename Method>
EnterHostFromHostResourceForceCallback(
const HostResource& host_resource,
CallbackFactory& factory,
Method method)
: EnterHostFromHostResource<ResourceT>(host_resource,
factory.NewOptionalCallback(method)),
needs_running_(true) {
if (this->failed())
RunCallback(PP_ERROR_BADRESOURCE);
}
// For callbacks that take an extra parameter as a closure.
template<class CallbackFactory, typename Method, typename A>
EnterHostFromHostResourceForceCallback(
const HostResource& host_resource,
CallbackFactory& factory,
Method method,
const A& a)
: EnterHostFromHostResource<ResourceT>(host_resource,
factory.NewOptionalCallback(method, a)),
needs_running_(true) {
if (this->failed())
RunCallback(PP_ERROR_BADRESOURCE);
}
// For callbacks that take two extra parameters as a closure.
template<class CallbackFactory, typename Method, typename A, typename B>
EnterHostFromHostResourceForceCallback(
const HostResource& host_resource,
CallbackFactory& factory,
Method method,
const A& a,
const B& b)
: EnterHostFromHostResource<ResourceT>(host_resource,
factory.NewOptionalCallback(method, a, b)),
needs_running_(true) {
if (this->failed())
RunCallback(PP_ERROR_BADRESOURCE);
}
// For callbacks that take three extra parameters as a closure.
template<class CallbackFactory, typename Method, typename A, typename B,
typename C>
EnterHostFromHostResourceForceCallback(
const HostResource& host_resource,
CallbackFactory& factory,
Method method,
const A& a,
const B& b,
const C& c)
: EnterHostFromHostResource<ResourceT>(host_resource,
factory.NewOptionalCallback(method, a, b, c)),
needs_running_(true) {
if (this->failed())
RunCallback(PP_ERROR_BADRESOURCE);
}
~EnterHostFromHostResourceForceCallback() {
if (needs_running_) {
NOTREACHED() << "Should always call SetResult except in the "
"initialization failed case.";
RunCallback(PP_ERROR_FAILED);
}
}
void SetResult(int32_t result) {
DCHECK(needs_running_) << "Don't call SetResult when there already is one.";
if (result != PP_OK_COMPLETIONPENDING)
RunCallback(result);
needs_running_ = false;
// Either we already ran the callback, or it will be run asynchronously. We
// clear the callback so it isn't accidentally run again (and because
// EnterBase checks that the callback has been cleared).
this->ClearCallback();
}
private:
void RunCallback(int32_t result) {
DCHECK(needs_running_);
needs_running_ = false;
this->callback()->Run(result);
this->ClearCallback();
}
bool needs_running_;
};
} // namespace proxy
} // namespace ppapi
#endif // PPAPI_PROXY_ENTER_PROXY_H_