[content shell] hook up testRunner.dumpEditingCallbacks
[chromium-blink-merge.git] / content / ppapi_plugin / ppapi_thread.cc
blob9b77af11703f70d527eb7ecc76852201466c4ca8
1 // Copyright (c) 2012 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
5 #include "content/ppapi_plugin/ppapi_thread.h"
7 #include <limits>
9 #include "base/command_line.h"
10 #include "base/process_util.h"
11 #include "base/rand_util.h"
12 #include "base/stringprintf.h"
13 #include "base/utf_string_conversions.h"
14 #include "content/common/child_process.h"
15 #include "content/common/child_process_messages.h"
16 #include "content/ppapi_plugin/broker_process_dispatcher.h"
17 #include "content/ppapi_plugin/plugin_process_dispatcher.h"
18 #include "content/ppapi_plugin/ppapi_webkitplatformsupport_impl.h"
19 #include "content/public/common/content_client.h"
20 #include "content/public/common/pepper_plugin_info.h"
21 #include "content/public/common/sandbox_init.h"
22 #include "content/public/plugin/content_plugin_client.h"
23 #include "ipc/ipc_channel_handle.h"
24 #include "ipc/ipc_platform_file.h"
25 #include "ipc/ipc_sync_channel.h"
26 #include "ipc/ipc_sync_message_filter.h"
27 #include "ppapi/c/dev/ppp_network_state_dev.h"
28 #include "ppapi/c/pp_errors.h"
29 #include "ppapi/c/ppp.h"
30 #include "ppapi/proxy/plugin_globals.h"
31 #include "ppapi/proxy/ppapi_messages.h"
32 #include "ppapi/proxy/interface_list.h"
33 #include "third_party/WebKit/Source/WebKit/chromium/public/WebKit.h"
34 #include "ui/base/ui_base_switches.h"
35 #include "webkit/plugins/plugin_switches.h"
37 #if defined(OS_WIN)
38 #include "base/win/win_util.h"
39 #include "sandbox/win/src/sandbox.h"
40 #elif defined(OS_MACOSX)
41 #include "content/common/sandbox_init_mac.h"
42 #endif
44 #if defined(OS_WIN)
45 extern sandbox::TargetServices* g_target_services;
46 #else
47 extern void* g_target_services;
48 #endif
50 namespace content {
52 typedef int32_t (*InitializeBrokerFunc)
53 (PP_ConnectInstance_Func* connect_instance_func);
55 PpapiThread::PpapiThread(const CommandLine& command_line, bool is_broker)
56 : is_broker_(is_broker),
57 connect_instance_func_(NULL),
58 local_pp_module_(
59 base::RandInt(0, std::numeric_limits<PP_Module>::max())),
60 next_plugin_dispatcher_id_(1) {
61 ppapi::proxy::PluginGlobals* globals = ppapi::proxy::PluginGlobals::Get();
62 globals->set_plugin_proxy_delegate(this);
63 globals->set_command_line(
64 command_line.GetSwitchValueASCII(switches::kPpapiFlashArgs));
65 if (command_line.HasSwitch(switches::kDisablePepperThreading)) {
66 globals->set_enable_threading(false);
67 } else if (command_line.HasSwitch(switches::kEnablePepperThreading)) {
68 globals->set_enable_threading(true);
69 } else {
70 globals->set_enable_threading(false);
73 webkit_platform_support_.reset(new PpapiWebKitPlatformSupportImpl);
74 WebKit::initialize(webkit_platform_support_.get());
77 PpapiThread::~PpapiThread() {
78 ppapi::proxy::PluginGlobals::Get()->set_plugin_proxy_delegate(NULL);
79 if (plugin_entry_points_.shutdown_module)
80 plugin_entry_points_.shutdown_module();
81 WebKit::shutdown();
83 #if defined(OS_WIN)
84 if (permissions_.HasPermission(ppapi::PERMISSION_FLASH))
85 base::win::SetShouldCrashOnProcessDetach(false);
86 #endif
89 bool PpapiThread::Send(IPC::Message* msg) {
90 // Allow access from multiple threads.
91 if (MessageLoop::current() == message_loop())
92 return ChildThread::Send(msg);
94 return sync_message_filter()->Send(msg);
97 // The "regular" ChildThread implements this function and does some standard
98 // dispatching, then uses the message router. We don't actually need any of
99 // this so this function just overrides that one.
101 // Note that this function is called only for messages from the channel to the
102 // browser process. Messages from the renderer process are sent via a different
103 // channel that ends up at Dispatcher::OnMessageReceived.
104 bool PpapiThread::OnMessageReceived(const IPC::Message& msg) {
105 IPC_BEGIN_MESSAGE_MAP(PpapiThread, msg)
106 IPC_MESSAGE_HANDLER(PpapiMsg_LoadPlugin, OnMsgLoadPlugin)
107 IPC_MESSAGE_HANDLER(PpapiMsg_CreateChannel, OnMsgCreateChannel)
109 IPC_MESSAGE_HANDLER(PpapiPluginMsg_ResourceReply, OnMsgResourceReply)
111 IPC_MESSAGE_HANDLER_GENERIC(PpapiMsg_PPBTCPServerSocket_ListenACK,
112 OnPluginDispatcherMessageReceived(msg))
113 IPC_MESSAGE_HANDLER_GENERIC(PpapiMsg_PPBTCPServerSocket_AcceptACK,
114 OnPluginDispatcherMessageReceived(msg))
115 IPC_MESSAGE_HANDLER_GENERIC(PpapiMsg_PPBTCPSocket_ConnectACK,
116 OnPluginDispatcherMessageReceived(msg))
117 IPC_MESSAGE_HANDLER_GENERIC(PpapiMsg_PPBTCPSocket_SSLHandshakeACK,
118 OnPluginDispatcherMessageReceived(msg))
119 IPC_MESSAGE_HANDLER_GENERIC(PpapiMsg_PPBTCPSocket_ReadACK,
120 OnPluginDispatcherMessageReceived(msg))
121 IPC_MESSAGE_HANDLER_GENERIC(PpapiMsg_PPBTCPSocket_WriteACK,
122 OnPluginDispatcherMessageReceived(msg))
123 IPC_MESSAGE_HANDLER_GENERIC(PpapiMsg_PPBUDPSocket_RecvFromACK,
124 OnPluginDispatcherMessageReceived(msg))
125 IPC_MESSAGE_HANDLER_GENERIC(PpapiMsg_PPBUDPSocket_SendToACK,
126 OnPluginDispatcherMessageReceived(msg))
127 IPC_MESSAGE_HANDLER_GENERIC(PpapiMsg_PPBUDPSocket_BindACK,
128 OnPluginDispatcherMessageReceived(msg))
129 IPC_MESSAGE_HANDLER_GENERIC(PpapiMsg_PPBHostResolver_ResolveACK,
130 OnPluginDispatcherMessageReceived(msg))
131 IPC_MESSAGE_HANDLER_GENERIC(PpapiMsg_PPBNetworkMonitor_NetworkList,
132 OnPluginDispatcherMessageReceived(msg))
133 IPC_MESSAGE_HANDLER(PpapiMsg_SetNetworkState, OnMsgSetNetworkState)
134 IPC_END_MESSAGE_MAP()
135 return true;
138 void PpapiThread::OnChannelConnected(int32 peer_pid) {
139 ChildThread::OnChannelConnected(peer_pid);
140 #if defined(OS_WIN)
141 if (is_broker_)
142 peer_handle_.Set(::OpenProcess(PROCESS_DUP_HANDLE, FALSE, peer_pid));
143 #endif
146 base::MessageLoopProxy* PpapiThread::GetIPCMessageLoop() {
147 return ChildProcess::current()->io_message_loop_proxy();
150 base::WaitableEvent* PpapiThread::GetShutdownEvent() {
151 return ChildProcess::current()->GetShutDownEvent();
154 IPC::PlatformFileForTransit PpapiThread::ShareHandleWithRemote(
155 base::PlatformFile handle,
156 const IPC::SyncChannel& channel,
157 bool should_close_source) {
158 #if defined(OS_WIN)
159 if (peer_handle_.IsValid()) {
160 DCHECK(is_broker_);
161 return IPC::GetFileHandleForProcess(handle, peer_handle_,
162 should_close_source);
164 #endif
166 return BrokerGetFileHandleForProcess(handle, channel.peer_pid(),
167 should_close_source);
170 std::set<PP_Instance>* PpapiThread::GetGloballySeenInstanceIDSet() {
171 return &globally_seen_instance_ids_;
174 IPC::Sender* PpapiThread::GetBrowserSender() {
175 return this;
178 std::string PpapiThread::GetUILanguage() {
179 CommandLine* command_line = CommandLine::ForCurrentProcess();
180 return command_line->GetSwitchValueASCII(switches::kLang);
183 void PpapiThread::PreCacheFont(const void* logfontw) {
184 #if defined(OS_WIN)
185 Send(new ChildProcessHostMsg_PreCacheFont(
186 *static_cast<const LOGFONTW*>(logfontw)));
187 #endif
190 void PpapiThread::SetActiveURL(const std::string& url) {
191 GetContentClient()->SetActiveURL(GURL(url));
194 uint32 PpapiThread::Register(ppapi::proxy::PluginDispatcher* plugin_dispatcher) {
195 if (!plugin_dispatcher ||
196 plugin_dispatchers_.size() >= std::numeric_limits<uint32>::max()) {
197 return 0;
200 uint32 id = 0;
201 do {
202 // Although it is unlikely, make sure that we won't cause any trouble when
203 // the counter overflows.
204 id = next_plugin_dispatcher_id_++;
205 } while (id == 0 ||
206 plugin_dispatchers_.find(id) != plugin_dispatchers_.end());
207 plugin_dispatchers_[id] = plugin_dispatcher;
208 return id;
211 void PpapiThread::Unregister(uint32 plugin_dispatcher_id) {
212 plugin_dispatchers_.erase(plugin_dispatcher_id);
215 void PpapiThread::OnMsgLoadPlugin(const FilePath& path,
216 const ppapi::PpapiPermissions& permissions) {
217 SavePluginName(path);
219 // This must be set before calling into the plugin so it can get the
220 // interfaces it has permission for.
221 ppapi::proxy::InterfaceList::SetProcessGlobalPermissions(permissions);
222 permissions_ = permissions;
224 // Trusted Pepper plugins may be "internal", i.e. built-in to the browser
225 // binary. If we're being asked to load such a plugin (e.g. the Chromoting
226 // client) then fetch the entry points from the embedder, rather than a DLL.
227 std::vector<content::PepperPluginInfo> plugins;
228 GetContentClient()->AddPepperPlugins(&plugins);
229 for (size_t i = 0; i < plugins.size(); ++i) {
230 if (plugins[i].is_internal && plugins[i].path == path) {
231 // An internal plugin is being loaded, so fetch the entry points.
232 plugin_entry_points_ = plugins[i].internal_entry_points;
236 // If the plugin isn't internal then load it from |path|.
237 base::ScopedNativeLibrary library;
238 if (plugin_entry_points_.initialize_module == NULL) {
239 // Load the plugin from the specified library.
240 std::string error;
241 library.Reset(base::LoadNativeLibrary(path, &error));
242 if (!library.is_valid()) {
243 LOG(ERROR) << "Failed to load Pepper module from "
244 << path.value() << " (error: " << error << ")";
245 return;
248 // Get the GetInterface function (required).
249 plugin_entry_points_.get_interface =
250 reinterpret_cast<PP_GetInterface_Func>(
251 library.GetFunctionPointer("PPP_GetInterface"));
252 if (!plugin_entry_points_.get_interface) {
253 LOG(WARNING) << "No PPP_GetInterface in plugin library";
254 return;
257 // The ShutdownModule/ShutdownBroker function is optional.
258 plugin_entry_points_.shutdown_module =
259 is_broker_ ?
260 reinterpret_cast<PP_ShutdownModule_Func>(
261 library.GetFunctionPointer("PPP_ShutdownBroker")) :
262 reinterpret_cast<PP_ShutdownModule_Func>(
263 library.GetFunctionPointer("PPP_ShutdownModule"));
265 if (!is_broker_) {
266 // Get the InitializeModule function (required for non-broker code).
267 plugin_entry_points_.initialize_module =
268 reinterpret_cast<PP_InitializeModule_Func>(
269 library.GetFunctionPointer("PPP_InitializeModule"));
270 if (!plugin_entry_points_.initialize_module) {
271 LOG(WARNING) << "No PPP_InitializeModule in plugin library";
272 return;
277 #if defined(OS_WIN)
278 // If code subsequently tries to exit using abort(), force a crash (since
279 // otherwise these would be silent terminations and fly under the radar).
280 base::win::SetAbortBehaviorForCrashReporting();
281 if (permissions.HasPermission(ppapi::PERMISSION_FLASH)) {
282 // Force a crash for exit(), _exit(), or ExitProcess(), but only do that for
283 // Pepper Flash.
284 base::win::SetShouldCrashOnProcessDetach(true);
287 // Once we lower the token the sandbox is locked down and no new modules
288 // can be loaded. TODO(cpu): consider changing to the loading style of
289 // regular plugins.
290 if (g_target_services) {
291 // Cause advapi32 to load before the sandbox is turned on.
292 unsigned int dummy_rand;
293 rand_s(&dummy_rand);
294 // Warm up language subsystems before the sandbox is turned on.
295 ::GetUserDefaultLangID();
296 ::GetUserDefaultLCID();
298 g_target_services->LowerToken();
300 #endif
302 if (is_broker_) {
303 // Get the InitializeBroker function (required).
304 InitializeBrokerFunc init_broker =
305 reinterpret_cast<InitializeBrokerFunc>(
306 library.GetFunctionPointer("PPP_InitializeBroker"));
307 if (!init_broker) {
308 LOG(WARNING) << "No PPP_InitializeBroker in plugin library";
309 return;
312 int32_t init_error = init_broker(&connect_instance_func_);
313 if (init_error != PP_OK) {
314 LOG(WARNING) << "InitBroker failed with error " << init_error;
315 return;
317 if (!connect_instance_func_) {
318 LOG(WARNING) << "InitBroker did not provide PP_ConnectInstance_Func";
319 return;
321 } else {
322 #if defined(OS_MACOSX)
323 // We need to do this after getting |PPP_GetInterface()| (or presumably
324 // doing something nontrivial with the library), else the sandbox
325 // intercedes.
326 CHECK(InitializeSandbox());
327 #endif
329 int32_t init_error = plugin_entry_points_.initialize_module(
330 local_pp_module_,
331 &ppapi::proxy::PluginDispatcher::GetBrowserInterface);
332 if (init_error != PP_OK) {
333 LOG(WARNING) << "InitModule failed with error " << init_error;
334 return;
338 // Initialization succeeded, so keep the plugin DLL loaded.
339 library_.Reset(library.Release());
342 void PpapiThread::OnMsgCreateChannel(int renderer_id, bool incognito) {
343 IPC::ChannelHandle channel_handle;
345 if (!plugin_entry_points_.get_interface || // Plugin couldn't be loaded.
346 !SetupRendererChannel(renderer_id, incognito, &channel_handle)) {
347 Send(new PpapiHostMsg_ChannelCreated(IPC::ChannelHandle()));
348 return;
351 Send(new PpapiHostMsg_ChannelCreated(channel_handle));
354 void PpapiThread::OnMsgResourceReply(
355 const ppapi::proxy::ResourceMessageReplyParams& reply_params,
356 const IPC::Message& nested_msg) {
357 ppapi::proxy::PluginDispatcher::DispatchResourceReply(reply_params,
358 nested_msg);
361 void PpapiThread::OnMsgSetNetworkState(bool online) {
362 // Note the browser-process side shouldn't send us these messages in the
363 // first unless the plugin has dev permissions, so we don't need to check
364 // again here. We don't want random plugins depending on this dev interface.
365 if (!plugin_entry_points_.get_interface)
366 return;
367 const PPP_NetworkState_Dev* ns = static_cast<const PPP_NetworkState_Dev*>(
368 plugin_entry_points_.get_interface(PPP_NETWORK_STATE_DEV_INTERFACE));
369 if (ns)
370 ns->SetOnLine(PP_FromBool(online));
373 void PpapiThread::OnPluginDispatcherMessageReceived(const IPC::Message& msg) {
374 // The first parameter should be a plugin dispatcher ID.
375 PickleIterator iter(msg);
376 uint32 id = 0;
377 if (!msg.ReadUInt32(&iter, &id)) {
378 NOTREACHED();
379 return;
381 std::map<uint32, ppapi::proxy::PluginDispatcher*>::iterator dispatcher =
382 plugin_dispatchers_.find(id);
383 if (dispatcher != plugin_dispatchers_.end())
384 dispatcher->second->OnMessageReceived(msg);
387 bool PpapiThread::SetupRendererChannel(int renderer_id,
388 bool incognito,
389 IPC::ChannelHandle* handle) {
390 DCHECK(is_broker_ == (connect_instance_func_ != NULL));
391 IPC::ChannelHandle plugin_handle;
392 plugin_handle.name = IPC::Channel::GenerateVerifiedChannelID(
393 StringPrintf("%d.r%d", base::GetCurrentProcId(), renderer_id));
395 ppapi::proxy::ProxyChannel* dispatcher = NULL;
396 bool init_result = false;
397 if (is_broker_) {
398 BrokerProcessDispatcher* broker_dispatcher =
399 new BrokerProcessDispatcher(plugin_entry_points_.get_interface,
400 connect_instance_func_);
401 init_result = broker_dispatcher->InitBrokerWithChannel(this,
402 plugin_handle,
403 false);
404 dispatcher = broker_dispatcher;
405 } else {
406 PluginProcessDispatcher* plugin_dispatcher =
407 new PluginProcessDispatcher(plugin_entry_points_.get_interface,
408 permissions_,
409 incognito);
410 init_result = plugin_dispatcher->InitPluginWithChannel(this,
411 plugin_handle,
412 false);
413 dispatcher = plugin_dispatcher;
416 if (!init_result) {
417 delete dispatcher;
418 return false;
421 handle->name = plugin_handle.name;
422 #if defined(OS_POSIX)
423 // On POSIX, transfer ownership of the renderer-side (client) FD.
424 // This ensures this process will be notified when it is closed even if a
425 // connection is not established.
426 handle->socket = base::FileDescriptor(dispatcher->TakeRendererFD(), true);
427 if (handle->socket.fd == -1)
428 return false;
429 #endif
431 // From here, the dispatcher will manage its own lifetime according to the
432 // lifetime of the attached channel.
433 return true;
436 void PpapiThread::SavePluginName(const FilePath& path) {
437 ppapi::proxy::PluginGlobals::Get()->set_plugin_name(
438 path.BaseName().AsUTF8Unsafe());
440 // plugin() is NULL when in-process. Which is fine, because this is
441 // just a hook for setting the process name.
442 if (GetContentClient()->plugin()) {
443 GetContentClient()->plugin()->PluginProcessStarted(
444 path.BaseName().RemoveExtension().LossyDisplayName());
448 } // namespace content