blob: bb42fcb7a61b1a0354c02ee617d11a65057a00f2 [file] [log] [blame]
[email protected]a08ebea2011-02-13 17:50:201// Copyright (c) 2011 The Chromium Authors. All rights reserved.
[email protected]c2932f5e2010-11-03 03:22:332// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5#include "ppapi/proxy/plugin_dispatcher.h"
6
7#include <map>
8
[email protected]709a847e2010-11-10 01:16:119#include "base/compiler_specific.h"
[email protected]c2932f5e2010-11-03 03:22:3310#include "base/logging.h"
[email protected]aabfa652011-07-13 23:28:4411#include "base/message_loop.h"
[email protected]c2932f5e2010-11-03 03:22:3312#include "ipc/ipc_message.h"
13#include "ipc/ipc_sync_channel.h"
[email protected]366ae242011-05-10 02:23:5814#include "base/debug/trace_event.h"
[email protected]c2932f5e2010-11-03 03:22:3315#include "ppapi/c/pp_errors.h"
16#include "ppapi/proxy/interface_proxy.h"
[email protected]2cc062242011-03-10 21:16:3417#include "ppapi/proxy/plugin_message_filter.h"
[email protected]6239d342011-05-06 22:55:4718#include "ppapi/proxy/plugin_resource_tracker.h"
[email protected]c2932f5e2010-11-03 03:22:3319#include "ppapi/proxy/plugin_var_serialization_rules.h"
20#include "ppapi/proxy/ppapi_messages.h"
[email protected]9815108e2011-05-27 21:50:2821#include "ppapi/proxy/ppb_char_set_proxy.h"
22#include "ppapi/proxy/ppb_cursor_control_proxy.h"
[email protected]d259a8e2011-05-18 22:31:0923#include "ppapi/proxy/ppb_font_proxy.h"
[email protected]ceadc392011-06-15 23:04:2424#include "ppapi/proxy/ppb_instance_proxy.h"
[email protected]c2932f5e2010-11-03 03:22:3325#include "ppapi/proxy/ppp_class_proxy.h"
[email protected]6239d342011-05-06 22:55:4726#include "ppapi/proxy/resource_creation_proxy.h"
[email protected]7f8b26b2011-08-18 15:41:0127#include "ppapi/shared_impl/resource.h"
[email protected]6239d342011-05-06 22:55:4728#include "ppapi/shared_impl/tracker_base.h"
[email protected]c2932f5e2010-11-03 03:22:3329
[email protected]a08ebea2011-02-13 17:50:2030#if defined(OS_POSIX)
31#include "base/eintr_wrapper.h"
32#include "ipc/ipc_channel_posix.h"
33#endif
34
[email protected]4d2efd22011-08-18 21:58:0235namespace ppapi {
[email protected]c2932f5e2010-11-03 03:22:3336namespace proxy {
37
38namespace {
39
[email protected]465faa22011-02-08 16:31:4640typedef std::map<PP_Instance, PluginDispatcher*> InstanceToDispatcherMap;
41InstanceToDispatcherMap* g_instance_to_dispatcher = NULL;
[email protected]c2932f5e2010-11-03 03:22:3342
[email protected]c2932f5e2010-11-03 03:22:3343} // namespace
44
[email protected]c7bf7452011-09-12 21:31:5045InstanceData::InstanceData() : fullscreen(PP_FALSE) {
46 memset(&position, 0, sizeof(position));
47}
48
[email protected]5d84d012010-12-02 17:17:2149PluginDispatcher::PluginDispatcher(base::ProcessHandle remote_process_handle,
[email protected]a08ebea2011-02-13 17:50:2050 GetInterfaceFunc get_interface)
[email protected]d259a8e2011-05-18 22:31:0951 : Dispatcher(remote_process_handle, get_interface),
[email protected]208aad792011-05-26 19:05:2852 plugin_delegate_(NULL),
[email protected]373a95a2011-07-01 16:58:1453 received_preferences_(false),
54 plugin_dispatcher_id_(0) {
[email protected]4614f192011-01-21 00:26:4355 SetSerializationRules(new PluginVarSerializationRules);
[email protected]c77a8dd42011-09-12 20:37:0456
57 // As a plugin, we always support the PPP_Class interface. There's no
58 // GetInterface call or name for it, so we insert it into our table now.
59 target_proxies_[INTERFACE_ID_PPP_CLASS].reset(new PPP_Class_Proxy(this));
60
[email protected]4d2efd22011-08-18 21:58:0261 TrackerBase::Init(&PluginResourceTracker::GetTrackerBaseInstance);
[email protected]c2932f5e2010-11-03 03:22:3362}
63
64PluginDispatcher::~PluginDispatcher() {
[email protected]373a95a2011-07-01 16:58:1465 if (plugin_delegate_)
66 plugin_delegate_->Unregister(plugin_dispatcher_id_);
[email protected]c2932f5e2010-11-03 03:22:3367}
68
69// static
[email protected]4614f192011-01-21 00:26:4370PluginDispatcher* PluginDispatcher::GetForInstance(PP_Instance instance) {
[email protected]465faa22011-02-08 16:31:4671 if (!g_instance_to_dispatcher)
72 return NULL;
73 InstanceToDispatcherMap::iterator found = g_instance_to_dispatcher->find(
74 instance);
75 if (found == g_instance_to_dispatcher->end())
76 return NULL;
77 return found->second;
[email protected]4614f192011-01-21 00:26:4378}
79
[email protected]a08ebea2011-02-13 17:50:2080// static
[email protected]7f8b26b2011-08-18 15:41:0181PluginDispatcher* PluginDispatcher::GetForResource(const Resource* resource) {
82 return GetForInstance(resource->pp_instance());
83}
84
85// static
[email protected]c77a8dd42011-09-12 20:37:0486const void* PluginDispatcher::GetInterfaceFromDispatcher(
87 const char* dispatcher_interface) {
88 // All interfaces the plugin requests of the browser are "PPB".
89 const InterfaceProxy::Info* info = GetPPBInterfaceInfo(dispatcher_interface);
90 if (!info)
91 return NULL;
92 return info->interface_ptr;
[email protected]a08ebea2011-02-13 17:50:2093}
94
[email protected]e2614c62011-04-16 22:12:4595bool PluginDispatcher::InitPluginWithChannel(
[email protected]d259a8e2011-05-18 22:31:0996 PluginDelegate* delegate,
[email protected]2cc062242011-03-10 21:16:3497 const IPC::ChannelHandle& channel_handle,
98 bool is_client) {
99 if (!Dispatcher::InitWithChannel(delegate, channel_handle, is_client))
100 return false;
[email protected]d259a8e2011-05-18 22:31:09101 plugin_delegate_ = delegate;
[email protected]373a95a2011-07-01 16:58:14102 plugin_dispatcher_id_ = plugin_delegate_->Register(this);
[email protected]2cc062242011-03-10 21:16:34103
104 // The message filter will intercept and process certain messages directly
105 // on the I/O thread.
106 channel()->AddFilter(
107 new PluginMessageFilter(delegate->GetGloballySeenInstanceIDSet()));
108 return true;
109}
110
[email protected]7cf40912010-12-09 18:25:03111bool PluginDispatcher::IsPlugin() const {
112 return true;
113}
114
[email protected]b00bbb32011-03-30 19:02:14115bool PluginDispatcher::Send(IPC::Message* msg) {
[email protected]366ae242011-05-10 02:23:58116 TRACE_EVENT2("ppapi proxy", "PluginDispatcher::Send",
117 "Class", IPC_MESSAGE_ID_CLASS(msg->type()),
118 "Line", IPC_MESSAGE_ID_LINE(msg->type()));
[email protected]b00bbb32011-03-30 19:02:14119 // We always want plugin->renderer messages to arrive in-order. If some sync
120 // and some async messages are send in response to a synchronous
121 // renderer->plugin call, the sync reply will be processed before the async
122 // reply, and everything will be confused.
123 //
124 // Allowing all async messages to unblock the renderer means more reentrancy
125 // there but gives correct ordering.
126 msg->set_unblock(true);
127 return Dispatcher::Send(msg);
128}
129
[email protected]a95986a82010-12-24 06:19:28130bool PluginDispatcher::OnMessageReceived(const IPC::Message& msg) {
[email protected]366ae242011-05-10 02:23:58131 TRACE_EVENT2("ppapi proxy", "PluginDispatcher::OnMessageReceived",
132 "Class", IPC_MESSAGE_ID_CLASS(msg.type()),
133 "Line", IPC_MESSAGE_ID_LINE(msg.type()));
[email protected]c77a8dd42011-09-12 20:37:04134 // Handle common control messages.
135 if (Dispatcher::OnMessageReceived(msg))
136 return true;
137
[email protected]c2932f5e2010-11-03 03:22:33138 if (msg.routing_id() == MSG_ROUTING_CONTROL) {
139 // Handle some plugin-specific control messages.
[email protected]a95986a82010-12-24 06:19:28140 bool handled = true;
[email protected]c2932f5e2010-11-03 03:22:33141 IPC_BEGIN_MESSAGE_MAP(PluginDispatcher, msg)
[email protected]465faa22011-02-08 16:31:46142 IPC_MESSAGE_HANDLER(PpapiMsg_SupportsInterface, OnMsgSupportsInterface)
[email protected]208aad792011-05-26 19:05:28143 IPC_MESSAGE_HANDLER(PpapiMsg_SetPreferences, OnMsgSetPreferences)
[email protected]c2932f5e2010-11-03 03:22:33144 IPC_END_MESSAGE_MAP()
[email protected]c77a8dd42011-09-12 20:37:04145 return handled;
[email protected]465faa22011-02-08 16:31:46146 }
[email protected]c77a8dd42011-09-12 20:37:04147
148 if (msg.routing_id() <= 0 || msg.routing_id() >= INTERFACE_ID_COUNT) {
149 // Host is sending us garbage. Since it's supposed to be trusted, this
150 // isn't supposed to happen. Crash here in all builds in case the renderer
151 // is compromised.
152 CHECK(false);
153 return true;
154 }
155
156 // There are two cases:
157 //
158 // * The first case is that the host is calling a PPP interface. It will
159 // always do a check for the interface before sending messages, and this
160 // will create the necessary interface proxy at that time. So when we
161 // actually receive a message, we know such a proxy will exist.
162 //
163 // * The second case is that the host is sending a response to the plugin
164 // side of a PPB interface (some, like the URL loader, have complex
165 // response messages). Since the host is trusted and not supposed to be
166 // doing silly things, we can just create a PPB proxy project on demand the
167 // first time it's needed.
168
169 InterfaceProxy* proxy = target_proxies_[msg.routing_id()].get();
170 if (!proxy) {
171 // Handle the first time the host calls a PPB reply interface by
172 // autocreating it.
173 const InterfaceProxy::Info* info = GetPPBInterfaceInfo(
174 static_cast<InterfaceID>(msg.routing_id()));
175 if (!info) {
176 NOTREACHED();
177 return true;
178 }
179 proxy = info->create_proxy(this, NULL);
180 target_proxies_[info->id].reset(proxy);
181 }
182
183 return proxy->OnMessageReceived(msg);
[email protected]c2932f5e2010-11-03 03:22:33184}
185
[email protected]a08ebea2011-02-13 17:50:20186void PluginDispatcher::OnChannelError() {
[email protected]4f15d2842011-02-15 17:36:33187 Dispatcher::OnChannelError();
188
[email protected]4b417e52011-04-18 22:51:08189 // The renderer has crashed or exited. This channel and all instances
190 // associated with it are no longer valid.
[email protected]a08ebea2011-02-13 17:50:20191 ForceFreeAllInstances();
192 // TODO(brettw) free resources too!
193 delete this;
194}
195
[email protected]f56279c2011-02-02 18:12:31196void PluginDispatcher::DidCreateInstance(PP_Instance instance) {
[email protected]465faa22011-02-08 16:31:46197 if (!g_instance_to_dispatcher)
198 g_instance_to_dispatcher = new InstanceToDispatcherMap;
199 (*g_instance_to_dispatcher)[instance] = this;
200
[email protected]f56279c2011-02-02 18:12:31201 instance_map_[instance] = InstanceData();
202}
203
204void PluginDispatcher::DidDestroyInstance(PP_Instance instance) {
205 InstanceDataMap::iterator it = instance_map_.find(instance);
206 if (it != instance_map_.end())
207 instance_map_.erase(it);
[email protected]465faa22011-02-08 16:31:46208
209 if (g_instance_to_dispatcher) {
210 InstanceToDispatcherMap::iterator found = g_instance_to_dispatcher->find(
211 instance);
212 if (found != g_instance_to_dispatcher->end()) {
213 DCHECK(found->second == this);
214 g_instance_to_dispatcher->erase(found);
215 } else {
216 NOTREACHED();
217 }
218 }
[email protected]f56279c2011-02-02 18:12:31219}
220
221InstanceData* PluginDispatcher::GetInstanceData(PP_Instance instance) {
222 InstanceDataMap::iterator it = instance_map_.find(instance);
223 return (it == instance_map_.end()) ? NULL : &it->second;
224}
225
[email protected]7a1f7c6f2011-05-10 21:17:48226void PluginDispatcher::PostToWebKitThread(
227 const tracked_objects::Location& from_here,
228 const base::Closure& task) {
[email protected]d259a8e2011-05-18 22:31:09229 return plugin_delegate_->PostToWebKitThread(from_here, task);
230}
231
232bool PluginDispatcher::SendToBrowser(IPC::Message* msg) {
233 return plugin_delegate_->SendToBrowser(msg);
[email protected]7a1f7c6f2011-05-10 21:17:48234}
235
[email protected]4d2efd22011-08-18 21:58:02236WebKitForwarding* PluginDispatcher::GetWebKitForwarding() {
[email protected]d259a8e2011-05-18 22:31:09237 return plugin_delegate_->GetWebKitForwarding();
[email protected]7a1f7c6f2011-05-10 21:17:48238}
239
[email protected]4d2efd22011-08-18 21:58:02240FunctionGroupBase* PluginDispatcher::GetFunctionAPI(InterfaceID id) {
[email protected]c77a8dd42011-09-12 20:37:04241 scoped_ptr<FunctionGroupBase >& proxy = function_proxies_[id];
242
243 if (proxy.get())
244 return proxy.get();
245
246 if (id == INTERFACE_ID_PPB_CHAR_SET)
247 proxy.reset(new PPB_CharSet_Proxy(this, NULL));
248 else if(id == INTERFACE_ID_PPB_CURSORCONTROL)
249 proxy.reset(new PPB_CursorControl_Proxy(this, NULL));
250 else if (id == INTERFACE_ID_PPB_FONT)
251 proxy.reset(new PPB_Font_Proxy(this, NULL));
252 else if (id == INTERFACE_ID_PPB_INSTANCE)
253 proxy.reset(new PPB_Instance_Proxy(this, NULL));
254 else if (id == INTERFACE_ID_RESOURCE_CREATION)
255 proxy.reset(new ResourceCreationProxy(this));
256
257 return proxy.get();
[email protected]6239d342011-05-06 22:55:47258}
259
[email protected]a08ebea2011-02-13 17:50:20260void PluginDispatcher::ForceFreeAllInstances() {
[email protected]4f15d2842011-02-15 17:36:33261 if (!g_instance_to_dispatcher)
262 return;
263
264 // Iterating will remove each item from the map, so we need to make a copy
265 // to avoid things changing out from under is.
266 InstanceToDispatcherMap temp_map = *g_instance_to_dispatcher;
267 for (InstanceToDispatcherMap::iterator i = temp_map.begin();
268 i != temp_map.end(); ++i) {
269 if (i->second == this) {
270 // Synthesize an "instance destroyed" message, this will notify the
271 // plugin and also remove it from our list of tracked plugins.
[email protected]4585fbc2011-06-13 17:17:56272 PpapiMsg_PPPInstance_DidDestroy msg(INTERFACE_ID_PPP_INSTANCE, i->first);
273 OnMessageReceived(msg);
[email protected]4f15d2842011-02-15 17:36:33274 }
275 }
[email protected]176c73922010-12-03 17:32:19276}
277
[email protected]465faa22011-02-08 16:31:46278void PluginDispatcher::OnMsgSupportsInterface(
279 const std::string& interface_name,
280 bool* result) {
[email protected]c77a8dd42011-09-12 20:37:04281 *result = false;
282
283 // Setup a proxy for receiving the messages from this interface.
284 const InterfaceProxy::Info* info = GetPPPInterfaceInfo(interface_name);
285 if (!info)
286 return; // Interface not supported by proxy.
287
288 // Check for a cached result.
289 if (target_proxies_[info->id].get()) {
290 *result = true;
291 return;
292 }
293
294 // Query the plugin & cache the result.
295 const void* interface_functions = GetLocalInterface(interface_name.c_str());
296 if (!interface_functions)
297 return;
298 target_proxies_[info->id].reset(
299 info->create_proxy(this, interface_functions));
300 *result = true;
[email protected]465faa22011-02-08 16:31:46301}
302
[email protected]4d2efd22011-08-18 21:58:02303void PluginDispatcher::OnMsgSetPreferences(const Preferences& prefs) {
[email protected]208aad792011-05-26 19:05:28304 // The renderer may send us preferences more than once (currently this
305 // happens every time a new plugin instance is created). Since we don't have
306 // a way to signal to the plugin that the preferences have changed, changing
307 // the default fonts and such in the middle of a running plugin could be
308 // confusing to it. As a result, we never allow the preferences to be changed
309 // once they're set. The user will have to restart to get new font prefs
310 // propogated to plugins.
311 if (!received_preferences_) {
312 received_preferences_ = true;
313 preferences_ = prefs;
314 }
315}
316
[email protected]c2932f5e2010-11-03 03:22:33317} // namespace proxy
[email protected]4d2efd22011-08-18 21:58:02318} // namespace ppapi