Use Persistent::Reset.
[chromium-blink-merge.git] / content / ppapi_plugin / ppapi_thread.cc
blob4f4be32b4b429636e94f20afa61e79192d790fa4
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/debug/crash_logging.h"
11 #include "base/logging.h"
12 #include "base/metrics/histogram.h"
13 #include "base/process_util.h"
14 #include "base/rand_util.h"
15 #include "base/stringprintf.h"
16 #include "base/threading/platform_thread.h"
17 #include "base/time.h"
18 #include "base/utf_string_conversions.h"
19 #include "content/common/child_process.h"
20 #include "content/common/child_process_messages.h"
21 #include "content/common/sandbox_util.h"
22 #include "content/ppapi_plugin/broker_process_dispatcher.h"
23 #include "content/ppapi_plugin/plugin_process_dispatcher.h"
24 #include "content/ppapi_plugin/ppapi_webkitplatformsupport_impl.h"
25 #include "content/public/common/content_client.h"
26 #include "content/public/common/pepper_plugin_info.h"
27 #include "content/public/common/sandbox_init.h"
28 #include "content/public/plugin/content_plugin_client.h"
29 #include "ipc/ipc_channel_handle.h"
30 #include "ipc/ipc_platform_file.h"
31 #include "ipc/ipc_sync_channel.h"
32 #include "ipc/ipc_sync_message_filter.h"
33 #include "ppapi/c/dev/ppp_network_state_dev.h"
34 #include "ppapi/c/pp_errors.h"
35 #include "ppapi/c/ppp.h"
36 #include "ppapi/proxy/plugin_globals.h"
37 #include "ppapi/proxy/ppapi_messages.h"
38 #include "ppapi/proxy/interface_list.h"
39 #include "ppapi/shared_impl/api_id.h"
40 #include "third_party/WebKit/Source/WebKit/chromium/public/WebKit.h"
41 #include "ui/base/ui_base_switches.h"
42 #include "webkit/plugins/plugin_switches.h"
44 #if defined(OS_WIN)
45 #include "base/win/win_util.h"
46 #include "sandbox/win/src/sandbox.h"
47 #elif defined(OS_MACOSX)
48 #include "content/common/sandbox_init_mac.h"
49 #endif
51 #if defined(OS_WIN)
52 extern sandbox::TargetServices* g_target_services;
53 #else
54 extern void* g_target_services;
55 #endif
57 namespace content {
59 typedef int32_t (*InitializeBrokerFunc)
60 (PP_ConnectInstance_Func* connect_instance_func);
62 PpapiThread::DispatcherMessageListener::DispatcherMessageListener(
63 PpapiThread* owner) : owner_(owner) {
66 PpapiThread::DispatcherMessageListener::~DispatcherMessageListener() {
69 bool PpapiThread::DispatcherMessageListener::OnMessageReceived(
70 const IPC::Message& msg) {
71 // The first parameter should be a plugin dispatcher ID.
72 PickleIterator iter(msg);
73 uint32 id = 0;
74 if (!msg.ReadUInt32(&iter, &id)) {
75 NOTREACHED();
76 return false;
78 std::map<uint32, ppapi::proxy::PluginDispatcher*>::iterator dispatcher =
79 owner_->plugin_dispatchers_.find(id);
80 if (dispatcher != owner_->plugin_dispatchers_.end())
81 return dispatcher->second->OnMessageReceived(msg);
83 return false;
86 PpapiThread::PpapiThread(const CommandLine& command_line, bool is_broker)
87 : is_broker_(is_broker),
88 connect_instance_func_(NULL),
89 local_pp_module_(
90 base::RandInt(0, std::numeric_limits<PP_Module>::max())),
91 next_plugin_dispatcher_id_(1),
92 dispatcher_message_listener_(this) {
93 ppapi::proxy::PluginGlobals* globals = ppapi::proxy::PluginGlobals::Get();
94 globals->set_plugin_proxy_delegate(this);
95 globals->set_command_line(
96 command_line.GetSwitchValueASCII(switches::kPpapiFlashArgs));
98 webkit_platform_support_.reset(new PpapiWebKitPlatformSupportImpl);
99 WebKit::initialize(webkit_platform_support_.get());
101 // Register interfaces that expect messages from the browser process. Please
102 // note that only those InterfaceProxy-based ones require registration.
103 AddRoute(ppapi::API_ID_PPB_TCPSERVERSOCKET_PRIVATE,
104 &dispatcher_message_listener_);
105 AddRoute(ppapi::API_ID_PPB_TCPSOCKET_PRIVATE,
106 &dispatcher_message_listener_);
107 AddRoute(ppapi::API_ID_PPB_UDPSOCKET_PRIVATE,
108 &dispatcher_message_listener_);
109 AddRoute(ppapi::API_ID_PPB_HOSTRESOLVER_PRIVATE,
110 &dispatcher_message_listener_);
111 AddRoute(ppapi::API_ID_PPB_NETWORKMANAGER_PRIVATE,
112 &dispatcher_message_listener_);
115 PpapiThread::~PpapiThread() {
118 void PpapiThread::Shutdown() {
119 ppapi::proxy::PluginGlobals::Get()->set_plugin_proxy_delegate(NULL);
120 if (plugin_entry_points_.shutdown_module)
121 plugin_entry_points_.shutdown_module();
122 WebKit::shutdown();
124 #if defined(OS_WIN)
125 if (permissions_.HasPermission(ppapi::PERMISSION_FLASH))
126 base::win::SetShouldCrashOnProcessDetach(false);
127 #endif
130 bool PpapiThread::Send(IPC::Message* msg) {
131 // Allow access from multiple threads.
132 if (base::MessageLoop::current() == message_loop())
133 return ChildThread::Send(msg);
135 return sync_message_filter()->Send(msg);
138 // Note that this function is called only for messages from the channel to the
139 // browser process. Messages from the renderer process are sent via a different
140 // channel that ends up at Dispatcher::OnMessageReceived.
141 bool PpapiThread::OnControlMessageReceived(const IPC::Message& msg) {
142 bool handled = true;
143 IPC_BEGIN_MESSAGE_MAP(PpapiThread, msg)
144 IPC_MESSAGE_HANDLER(PpapiMsg_LoadPlugin, OnLoadPlugin)
145 IPC_MESSAGE_HANDLER(PpapiMsg_CreateChannel, OnCreateChannel)
146 IPC_MESSAGE_HANDLER(PpapiMsg_SetNetworkState, OnSetNetworkState)
147 IPC_MESSAGE_HANDLER(PpapiMsg_Crash, OnCrash)
148 IPC_MESSAGE_HANDLER(PpapiMsg_Hang, OnHang)
149 IPC_MESSAGE_HANDLER(PpapiPluginMsg_ResourceReply, OnResourceReply)
150 IPC_MESSAGE_UNHANDLED(handled = false)
151 IPC_END_MESSAGE_MAP()
152 return handled;
155 void PpapiThread::OnChannelConnected(int32 peer_pid) {
156 ChildThread::OnChannelConnected(peer_pid);
157 #if defined(OS_WIN)
158 if (is_broker_)
159 peer_handle_.Set(::OpenProcess(PROCESS_DUP_HANDLE, FALSE, peer_pid));
160 #endif
163 base::MessageLoopProxy* PpapiThread::GetIPCMessageLoop() {
164 return ChildProcess::current()->io_message_loop_proxy();
167 base::WaitableEvent* PpapiThread::GetShutdownEvent() {
168 return ChildProcess::current()->GetShutDownEvent();
171 IPC::PlatformFileForTransit PpapiThread::ShareHandleWithRemote(
172 base::PlatformFile handle,
173 base::ProcessId peer_pid,
174 bool should_close_source) {
175 #if defined(OS_WIN)
176 if (peer_handle_.IsValid()) {
177 DCHECK(is_broker_);
178 return IPC::GetFileHandleForProcess(handle, peer_handle_,
179 should_close_source);
181 #endif
183 DCHECK(peer_pid != base::kNullProcessId);
184 return BrokerGetFileHandleForProcess(handle, peer_pid, should_close_source);
187 std::set<PP_Instance>* PpapiThread::GetGloballySeenInstanceIDSet() {
188 return &globally_seen_instance_ids_;
191 IPC::Sender* PpapiThread::GetBrowserSender() {
192 return this;
195 std::string PpapiThread::GetUILanguage() {
196 CommandLine* command_line = CommandLine::ForCurrentProcess();
197 return command_line->GetSwitchValueASCII(switches::kLang);
200 void PpapiThread::PreCacheFont(const void* logfontw) {
201 #if defined(OS_WIN)
202 Send(new ChildProcessHostMsg_PreCacheFont(
203 *static_cast<const LOGFONTW*>(logfontw)));
204 #endif
207 void PpapiThread::SetActiveURL(const std::string& url) {
208 GetContentClient()->SetActiveURL(GURL(url));
211 uint32 PpapiThread::Register(ppapi::proxy::PluginDispatcher* plugin_dispatcher) {
212 if (!plugin_dispatcher ||
213 plugin_dispatchers_.size() >= std::numeric_limits<uint32>::max()) {
214 return 0;
217 uint32 id = 0;
218 do {
219 // Although it is unlikely, make sure that we won't cause any trouble when
220 // the counter overflows.
221 id = next_plugin_dispatcher_id_++;
222 } while (id == 0 ||
223 plugin_dispatchers_.find(id) != plugin_dispatchers_.end());
224 plugin_dispatchers_[id] = plugin_dispatcher;
225 return id;
228 void PpapiThread::Unregister(uint32 plugin_dispatcher_id) {
229 plugin_dispatchers_.erase(plugin_dispatcher_id);
232 void PpapiThread::OnLoadPlugin(const base::FilePath& path,
233 const ppapi::PpapiPermissions& permissions) {
234 // In case of crashes, the crash dump doesn't indicate which plugin
235 // it came from.
236 base::debug::SetCrashKeyValue("ppapi_path", path.MaybeAsASCII());
238 SavePluginName(path);
240 // This must be set before calling into the plugin so it can get the
241 // interfaces it has permission for.
242 ppapi::proxy::InterfaceList::SetProcessGlobalPermissions(permissions);
243 permissions_ = permissions;
245 // Trusted Pepper plugins may be "internal", i.e. built-in to the browser
246 // binary. If we're being asked to load such a plugin (e.g. the Chromoting
247 // client) then fetch the entry points from the embedder, rather than a DLL.
248 std::vector<PepperPluginInfo> plugins;
249 GetContentClient()->AddPepperPlugins(&plugins);
250 for (size_t i = 0; i < plugins.size(); ++i) {
251 if (plugins[i].is_internal && plugins[i].path == path) {
252 // An internal plugin is being loaded, so fetch the entry points.
253 plugin_entry_points_ = plugins[i].internal_entry_points;
257 // If the plugin isn't internal then load it from |path|.
258 base::ScopedNativeLibrary library;
259 if (plugin_entry_points_.initialize_module == NULL) {
260 // Load the plugin from the specified library.
261 std::string error;
262 library.Reset(base::LoadNativeLibrary(path, &error));
263 if (!library.is_valid()) {
264 LOG(ERROR) << "Failed to load Pepper module from "
265 << path.value() << " (error: " << error << ")";
266 ReportLoadResult(path, LOAD_FAILED);
267 return;
270 // Get the GetInterface function (required).
271 plugin_entry_points_.get_interface =
272 reinterpret_cast<PP_GetInterface_Func>(
273 library.GetFunctionPointer("PPP_GetInterface"));
274 if (!plugin_entry_points_.get_interface) {
275 LOG(WARNING) << "No PPP_GetInterface in plugin library";
276 ReportLoadResult(path, ENTRY_POINT_MISSING);
277 return;
280 // The ShutdownModule/ShutdownBroker function is optional.
281 plugin_entry_points_.shutdown_module =
282 is_broker_ ?
283 reinterpret_cast<PP_ShutdownModule_Func>(
284 library.GetFunctionPointer("PPP_ShutdownBroker")) :
285 reinterpret_cast<PP_ShutdownModule_Func>(
286 library.GetFunctionPointer("PPP_ShutdownModule"));
288 if (!is_broker_) {
289 // Get the InitializeModule function (required for non-broker code).
290 plugin_entry_points_.initialize_module =
291 reinterpret_cast<PP_InitializeModule_Func>(
292 library.GetFunctionPointer("PPP_InitializeModule"));
293 if (!plugin_entry_points_.initialize_module) {
294 LOG(WARNING) << "No PPP_InitializeModule in plugin library";
295 ReportLoadResult(path, ENTRY_POINT_MISSING);
296 return;
301 #if defined(OS_WIN)
302 // If code subsequently tries to exit using abort(), force a crash (since
303 // otherwise these would be silent terminations and fly under the radar).
304 base::win::SetAbortBehaviorForCrashReporting();
305 if (permissions.HasPermission(ppapi::PERMISSION_FLASH)) {
306 // Force a crash for exit(), _exit(), or ExitProcess(), but only do that for
307 // Pepper Flash.
308 base::win::SetShouldCrashOnProcessDetach(true);
311 // Once we lower the token the sandbox is locked down and no new modules
312 // can be loaded. TODO(cpu): consider changing to the loading style of
313 // regular plugins.
314 if (g_target_services) {
315 // Cause advapi32 to load before the sandbox is turned on.
316 unsigned int dummy_rand;
317 rand_s(&dummy_rand);
318 // Warm up language subsystems before the sandbox is turned on.
319 ::GetUserDefaultLangID();
320 ::GetUserDefaultLCID();
322 g_target_services->LowerToken();
324 #endif
326 if (is_broker_) {
327 // Get the InitializeBroker function (required).
328 InitializeBrokerFunc init_broker =
329 reinterpret_cast<InitializeBrokerFunc>(
330 library.GetFunctionPointer("PPP_InitializeBroker"));
331 if (!init_broker) {
332 LOG(WARNING) << "No PPP_InitializeBroker in plugin library";
333 ReportLoadResult(path, ENTRY_POINT_MISSING);
334 return;
337 int32_t init_error = init_broker(&connect_instance_func_);
338 if (init_error != PP_OK) {
339 LOG(WARNING) << "InitBroker failed with error " << init_error;
340 ReportLoadResult(path, INIT_FAILED);
341 return;
343 if (!connect_instance_func_) {
344 LOG(WARNING) << "InitBroker did not provide PP_ConnectInstance_Func";
345 ReportLoadResult(path, INIT_FAILED);
346 return;
348 } else {
349 #if defined(OS_MACOSX)
350 // We need to do this after getting |PPP_GetInterface()| (or presumably
351 // doing something nontrivial with the library), else the sandbox
352 // intercedes.
353 CHECK(InitializeSandbox());
354 #endif
356 int32_t init_error = plugin_entry_points_.initialize_module(
357 local_pp_module_,
358 &ppapi::proxy::PluginDispatcher::GetBrowserInterface);
359 if (init_error != PP_OK) {
360 LOG(WARNING) << "InitModule failed with error " << init_error;
361 ReportLoadResult(path, INIT_FAILED);
362 return;
366 // Initialization succeeded, so keep the plugin DLL loaded.
367 library_.Reset(library.Release());
369 ReportLoadResult(path, LOAD_SUCCESS);
372 void PpapiThread::OnCreateChannel(base::ProcessId renderer_pid,
373 int renderer_child_id,
374 bool incognito) {
375 IPC::ChannelHandle channel_handle;
377 if (!plugin_entry_points_.get_interface || // Plugin couldn't be loaded.
378 !SetupRendererChannel(renderer_pid, renderer_child_id, incognito,
379 &channel_handle)) {
380 Send(new PpapiHostMsg_ChannelCreated(IPC::ChannelHandle()));
381 return;
384 Send(new PpapiHostMsg_ChannelCreated(channel_handle));
387 void PpapiThread::OnResourceReply(
388 const ppapi::proxy::ResourceMessageReplyParams& reply_params,
389 const IPC::Message& nested_msg) {
390 ppapi::proxy::PluginDispatcher::DispatchResourceReply(reply_params,
391 nested_msg);
394 void PpapiThread::OnSetNetworkState(bool online) {
395 // Note the browser-process side shouldn't send us these messages in the
396 // first unless the plugin has dev permissions, so we don't need to check
397 // again here. We don't want random plugins depending on this dev interface.
398 if (!plugin_entry_points_.get_interface)
399 return;
400 const PPP_NetworkState_Dev* ns = static_cast<const PPP_NetworkState_Dev*>(
401 plugin_entry_points_.get_interface(PPP_NETWORK_STATE_DEV_INTERFACE));
402 if (ns)
403 ns->SetOnLine(PP_FromBool(online));
406 void PpapiThread::OnCrash() {
407 // Intentionally crash upon the request of the browser.
408 volatile int* null_pointer = NULL;
409 *null_pointer = 0;
412 void PpapiThread::OnHang() {
413 // Intentionally hang upon the request of the browser.
414 for (;;)
415 base::PlatformThread::Sleep(base::TimeDelta::FromSeconds(1));
418 bool PpapiThread::SetupRendererChannel(base::ProcessId renderer_pid,
419 int renderer_child_id,
420 bool incognito,
421 IPC::ChannelHandle* handle) {
422 DCHECK(is_broker_ == (connect_instance_func_ != NULL));
423 IPC::ChannelHandle plugin_handle;
424 plugin_handle.name = IPC::Channel::GenerateVerifiedChannelID(
425 base::StringPrintf(
426 "%d.r%d", base::GetCurrentProcId(), renderer_child_id));
428 ppapi::proxy::ProxyChannel* dispatcher = NULL;
429 bool init_result = false;
430 if (is_broker_) {
431 BrokerProcessDispatcher* broker_dispatcher =
432 new BrokerProcessDispatcher(plugin_entry_points_.get_interface,
433 connect_instance_func_);
434 init_result = broker_dispatcher->InitBrokerWithChannel(this,
435 renderer_pid,
436 plugin_handle,
437 false);
438 dispatcher = broker_dispatcher;
439 } else {
440 PluginProcessDispatcher* plugin_dispatcher =
441 new PluginProcessDispatcher(plugin_entry_points_.get_interface,
442 permissions_,
443 incognito);
444 init_result = plugin_dispatcher->InitPluginWithChannel(this,
445 renderer_pid,
446 plugin_handle,
447 false);
448 dispatcher = plugin_dispatcher;
451 if (!init_result) {
452 delete dispatcher;
453 return false;
456 handle->name = plugin_handle.name;
457 #if defined(OS_POSIX)
458 // On POSIX, transfer ownership of the renderer-side (client) FD.
459 // This ensures this process will be notified when it is closed even if a
460 // connection is not established.
461 handle->socket = base::FileDescriptor(dispatcher->TakeRendererFD(), true);
462 if (handle->socket.fd == -1)
463 return false;
464 #endif
466 // From here, the dispatcher will manage its own lifetime according to the
467 // lifetime of the attached channel.
468 return true;
471 void PpapiThread::SavePluginName(const base::FilePath& path) {
472 ppapi::proxy::PluginGlobals::Get()->set_plugin_name(
473 path.BaseName().AsUTF8Unsafe());
475 // plugin() is NULL when in-process, which is fine, because this is
476 // just a hook for setting the process name.
477 if (GetContentClient()->plugin()) {
478 GetContentClient()->plugin()->PluginProcessStarted(
479 path.BaseName().RemoveExtension().LossyDisplayName());
483 void PpapiThread::ReportLoadResult(const base::FilePath& path,
484 LoadResult result) {
485 DCHECK_LT(result, LOAD_RESULT_MAX);
487 std::ostringstream histogram_name;
488 histogram_name << "Plugin.Ppapi" << (is_broker_ ? "Broker" : "Plugin")
489 << "LoadResult_" << path.BaseName().MaybeAsASCII();
491 // Note: This leaks memory, which is expected behavior.
492 base::HistogramBase* histogram =
493 base::LinearHistogram::FactoryGet(
494 histogram_name.str(),
496 LOAD_RESULT_MAX,
497 LOAD_RESULT_MAX + 1,
498 base::HistogramBase::kUmaTargetedHistogramFlag);
500 histogram->Add(result);
503 } // namespace content