// 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 #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 class EnterPluginFromHostResource : public thunk::EnterResourceNoLock { public: explicit EnterPluginFromHostResource(const HostResource& host_resource) : thunk::EnterResourceNoLock( 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 class EnterHostFromHostResource : public thunk::EnterResourceNoLock { public: explicit EnterHostFromHostResource(const HostResource& host_resource) : thunk::EnterResourceNoLock(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(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 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 class EnterHostFromHostResourceForceCallback : public EnterHostFromHostResource { public: EnterHostFromHostResourceForceCallback( const HostResource& host_resource, const pp::CompletionCallback& callback) : EnterHostFromHostResource(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 EnterHostFromHostResourceForceCallback( const HostResource& host_resource, CallbackFactory& factory, Method method) : EnterHostFromHostResource(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 EnterHostFromHostResourceForceCallback( const HostResource& host_resource, CallbackFactory& factory, Method method, const A& a) : EnterHostFromHostResource(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 EnterHostFromHostResourceForceCallback( const HostResource& host_resource, CallbackFactory& factory, Method method, const A& a, const B& b) : EnterHostFromHostResource(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 EnterHostFromHostResourceForceCallback( const HostResource& host_resource, CallbackFactory& factory, Method method, const A& a, const B& b, const C& c) : EnterHostFromHostResource(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_