Add ICU message format support
[chromium-blink-merge.git] / content / ppapi_plugin / ppapi_thread.cc
blobd2202446eaaec87931fd669d47dde419f5178d51
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/cpu.h"
11 #include "base/debug/crash_logging.h"
12 #include "base/files/file_util.h"
13 #include "base/logging.h"
14 #include "base/memory/discardable_memory_allocator.h"
15 #include "base/metrics/histogram.h"
16 #include "base/metrics/sparse_histogram.h"
17 #include "base/rand_util.h"
18 #include "base/strings/stringprintf.h"
19 #include "base/strings/utf_string_conversions.h"
20 #include "base/threading/platform_thread.h"
21 #include "base/time/time.h"
22 #include "base/trace_event/trace_event.h"
23 #include "content/child/browser_font_resource_trusted.h"
24 #include "content/child/child_discardable_shared_memory_manager.h"
25 #include "content/child/child_process.h"
26 #include "content/common/child_process_messages.h"
27 #include "content/common/sandbox_util.h"
28 #include "content/ppapi_plugin/broker_process_dispatcher.h"
29 #include "content/ppapi_plugin/plugin_process_dispatcher.h"
30 #include "content/ppapi_plugin/ppapi_blink_platform_impl.h"
31 #include "content/public/common/content_client.h"
32 #include "content/public/common/content_switches.h"
33 #include "content/public/common/pepper_plugin_info.h"
34 #include "content/public/common/sandbox_init.h"
35 #include "content/public/plugin/content_plugin_client.h"
36 #include "ipc/ipc_channel_handle.h"
37 #include "ipc/ipc_platform_file.h"
38 #include "ipc/ipc_sync_channel.h"
39 #include "ipc/ipc_sync_message_filter.h"
40 #include "ppapi/c/dev/ppp_network_state_dev.h"
41 #include "ppapi/c/pp_errors.h"
42 #include "ppapi/c/ppp.h"
43 #include "ppapi/proxy/interface_list.h"
44 #include "ppapi/proxy/plugin_globals.h"
45 #include "ppapi/proxy/plugin_message_filter.h"
46 #include "ppapi/proxy/ppapi_messages.h"
47 #include "ppapi/proxy/resource_reply_thread_registrar.h"
48 #include "third_party/WebKit/public/web/WebKit.h"
49 #include "ui/base/ui_base_switches.h"
51 #if defined(OS_WIN)
52 #include "base/win/win_util.h"
53 #include "base/win/windows_version.h"
54 #include "sandbox/win/src/sandbox.h"
55 #elif defined(OS_MACOSX)
56 #include "content/common/sandbox_init_mac.h"
57 #endif
59 #if defined(OS_WIN)
60 const char kWidevineCdmAdapterFileName[] = "widevinecdmadapter.dll";
62 extern sandbox::TargetServices* g_target_services;
64 // Used by EnumSystemLocales for warming up.
65 static BOOL CALLBACK EnumLocalesProc(LPTSTR lpLocaleString) {
66 return TRUE;
69 static BOOL CALLBACK EnumLocalesProcEx(
70 LPWSTR lpLocaleString,
71 DWORD dwFlags,
72 LPARAM lParam) {
73 return TRUE;
76 // Warm up language subsystems before the sandbox is turned on.
77 static void WarmupWindowsLocales(const ppapi::PpapiPermissions& permissions) {
78 ::GetUserDefaultLangID();
79 ::GetUserDefaultLCID();
81 if (permissions.HasPermission(ppapi::PERMISSION_FLASH)) {
82 if (base::win::GetVersion() >= base::win::VERSION_VISTA) {
83 typedef BOOL (WINAPI *PfnEnumSystemLocalesEx)
84 (LOCALE_ENUMPROCEX, DWORD, LPARAM, LPVOID);
86 HMODULE handle_kern32 = GetModuleHandleW(L"Kernel32.dll");
87 PfnEnumSystemLocalesEx enum_sys_locales_ex =
88 reinterpret_cast<PfnEnumSystemLocalesEx>
89 (GetProcAddress(handle_kern32, "EnumSystemLocalesEx"));
91 enum_sys_locales_ex(EnumLocalesProcEx, LOCALE_WINDOWS, 0, 0);
92 } else {
93 EnumSystemLocalesW(EnumLocalesProc, LCID_INSTALLED);
98 #endif
100 namespace content {
102 typedef int32_t (*InitializeBrokerFunc)
103 (PP_ConnectInstance_Func* connect_instance_func);
105 PpapiThread::PpapiThread(const base::CommandLine& command_line, bool is_broker)
106 : is_broker_(is_broker),
107 plugin_globals_(GetIOTaskRunner()),
108 connect_instance_func_(NULL),
109 local_pp_module_(base::RandInt(0, std::numeric_limits<PP_Module>::max())),
110 next_plugin_dispatcher_id_(1) {
111 plugin_globals_.SetPluginProxyDelegate(this);
112 plugin_globals_.set_command_line(
113 command_line.GetSwitchValueASCII(switches::kPpapiFlashArgs));
115 blink_platform_impl_.reset(new PpapiBlinkPlatformImpl);
116 blink::initialize(blink_platform_impl_.get());
118 if (!is_broker_) {
119 scoped_refptr<ppapi::proxy::PluginMessageFilter> plugin_filter(
120 new ppapi::proxy::PluginMessageFilter(
121 NULL, plugin_globals_.resource_reply_thread_registrar()));
122 channel()->AddFilter(plugin_filter.get());
123 plugin_globals_.RegisterResourceMessageFilters(plugin_filter.get());
126 // In single process, browser main loop set up the discardable memory
127 // allocator.
128 if (!command_line.HasSwitch(switches::kSingleProcess)) {
129 base::DiscardableMemoryAllocator::SetInstance(
130 ChildThreadImpl::discardable_shared_memory_manager());
134 PpapiThread::~PpapiThread() {
137 void PpapiThread::Shutdown() {
138 ChildThreadImpl::Shutdown();
140 ppapi::proxy::PluginGlobals::Get()->ResetPluginProxyDelegate();
141 if (plugin_entry_points_.shutdown_module)
142 plugin_entry_points_.shutdown_module();
143 blink_platform_impl_->Shutdown();
144 blink::shutdown();
147 bool PpapiThread::Send(IPC::Message* msg) {
148 // Allow access from multiple threads.
149 if (base::MessageLoop::current() == message_loop())
150 return ChildThreadImpl::Send(msg);
152 return sync_message_filter()->Send(msg);
155 // Note that this function is called only for messages from the channel to the
156 // browser process. Messages from the renderer process are sent via a different
157 // channel that ends up at Dispatcher::OnMessageReceived.
158 bool PpapiThread::OnControlMessageReceived(const IPC::Message& msg) {
159 bool handled = true;
160 IPC_BEGIN_MESSAGE_MAP(PpapiThread, msg)
161 IPC_MESSAGE_HANDLER(PpapiMsg_LoadPlugin, OnLoadPlugin)
162 IPC_MESSAGE_HANDLER(PpapiMsg_CreateChannel, OnCreateChannel)
163 IPC_MESSAGE_HANDLER(PpapiMsg_SetNetworkState, OnSetNetworkState)
164 IPC_MESSAGE_HANDLER(PpapiMsg_Crash, OnCrash)
165 IPC_MESSAGE_HANDLER(PpapiMsg_Hang, OnHang)
166 IPC_MESSAGE_UNHANDLED(handled = false)
167 IPC_END_MESSAGE_MAP()
168 return handled;
171 void PpapiThread::OnChannelConnected(int32 peer_pid) {
172 ChildThreadImpl::OnChannelConnected(peer_pid);
173 #if defined(OS_WIN)
174 if (is_broker_)
175 peer_handle_.Set(::OpenProcess(PROCESS_DUP_HANDLE, FALSE, peer_pid));
176 #endif
179 base::SingleThreadTaskRunner* PpapiThread::GetIPCTaskRunner() {
180 return ChildProcess::current()->io_task_runner();
183 base::WaitableEvent* PpapiThread::GetShutdownEvent() {
184 return ChildProcess::current()->GetShutDownEvent();
187 IPC::PlatformFileForTransit PpapiThread::ShareHandleWithRemote(
188 base::PlatformFile handle,
189 base::ProcessId peer_pid,
190 bool should_close_source) {
191 #if defined(OS_WIN)
192 if (peer_handle_.IsValid()) {
193 DCHECK(is_broker_);
194 return IPC::GetFileHandleForProcess(handle, peer_handle_.Get(),
195 should_close_source);
197 #endif
199 DCHECK(peer_pid != base::kNullProcessId);
200 return BrokerGetFileHandleForProcess(handle, peer_pid, should_close_source);
203 base::SharedMemoryHandle PpapiThread::ShareSharedMemoryHandleWithRemote(
204 const base::SharedMemoryHandle& handle,
205 base::ProcessId remote_pid) {
206 #if defined(OS_WIN)
207 if (peer_handle_.IsValid()) {
208 DCHECK(is_broker_);
209 return IPC::GetFileHandleForProcess(handle, peer_handle_.Get(), false);
211 #endif
213 DCHECK(remote_pid != base::kNullProcessId);
214 #if defined(OS_WIN) || defined(OS_MACOSX)
215 base::SharedMemoryHandle duped_handle;
216 bool success =
217 BrokerDuplicateSharedMemoryHandle(handle, remote_pid, &duped_handle);
218 if (success)
219 return duped_handle;
220 return base::SharedMemory::NULLHandle();
221 #else
222 return base::SharedMemory::DuplicateHandle(handle);
223 #endif // defined(OS_WIN) || defined(OS_MACOSX)
226 std::set<PP_Instance>* PpapiThread::GetGloballySeenInstanceIDSet() {
227 return &globally_seen_instance_ids_;
230 IPC::Sender* PpapiThread::GetBrowserSender() {
231 return this;
234 std::string PpapiThread::GetUILanguage() {
235 base::CommandLine* command_line = base::CommandLine::ForCurrentProcess();
236 return command_line->GetSwitchValueASCII(switches::kLang);
239 void PpapiThread::PreCacheFontForFlash(const void* logfontw) {
240 #if defined(OS_WIN)
241 ChildThreadImpl::PreCacheFont(*static_cast<const LOGFONTW*>(logfontw));
242 #endif
245 void PpapiThread::SetActiveURL(const std::string& url) {
246 GetContentClient()->SetActiveURL(GURL(url));
249 PP_Resource PpapiThread::CreateBrowserFont(
250 ppapi::proxy::Connection connection,
251 PP_Instance instance,
252 const PP_BrowserFont_Trusted_Description& desc,
253 const ppapi::Preferences& prefs) {
254 if (!BrowserFontResource_Trusted::IsPPFontDescriptionValid(desc))
255 return 0;
256 return (new BrowserFontResource_Trusted(
257 connection, instance, desc, prefs))->GetReference();
260 uint32 PpapiThread::Register(ppapi::proxy::PluginDispatcher* plugin_dispatcher) {
261 if (!plugin_dispatcher ||
262 plugin_dispatchers_.size() >= std::numeric_limits<uint32>::max()) {
263 return 0;
266 uint32 id = 0;
267 do {
268 // Although it is unlikely, make sure that we won't cause any trouble when
269 // the counter overflows.
270 id = next_plugin_dispatcher_id_++;
271 } while (id == 0 ||
272 plugin_dispatchers_.find(id) != plugin_dispatchers_.end());
273 plugin_dispatchers_[id] = plugin_dispatcher;
274 return id;
277 void PpapiThread::Unregister(uint32 plugin_dispatcher_id) {
278 plugin_dispatchers_.erase(plugin_dispatcher_id);
281 void PpapiThread::OnLoadPlugin(const base::FilePath& path,
282 const ppapi::PpapiPermissions& permissions) {
283 // In case of crashes, the crash dump doesn't indicate which plugin
284 // it came from.
285 base::debug::SetCrashKeyValue("ppapi_path", path.MaybeAsASCII());
287 SavePluginName(path);
289 // This must be set before calling into the plugin so it can get the
290 // interfaces it has permission for.
291 ppapi::proxy::InterfaceList::SetProcessGlobalPermissions(permissions);
292 permissions_ = permissions;
294 // Trusted Pepper plugins may be "internal", i.e. built-in to the browser
295 // binary. If we're being asked to load such a plugin (e.g. the Chromoting
296 // client) then fetch the entry points from the embedder, rather than a DLL.
297 std::vector<PepperPluginInfo> plugins;
298 GetContentClient()->AddPepperPlugins(&plugins);
299 for (size_t i = 0; i < plugins.size(); ++i) {
300 if (plugins[i].is_internal && plugins[i].path == path) {
301 // An internal plugin is being loaded, so fetch the entry points.
302 plugin_entry_points_ = plugins[i].internal_entry_points;
306 // If the plugin isn't internal then load it from |path|.
307 base::ScopedNativeLibrary library;
308 if (plugin_entry_points_.initialize_module == NULL) {
309 // Load the plugin from the specified library.
310 base::NativeLibraryLoadError error;
311 base::TimeDelta load_time;
313 TRACE_EVENT1("ppapi", "PpapiThread::LoadPlugin", "path",
314 path.MaybeAsASCII());
316 base::TimeTicks start = base::TimeTicks::Now();
317 library.Reset(base::LoadNativeLibrary(path, &error));
318 load_time = base::TimeTicks::Now() - start;
321 if (!library.is_valid()) {
322 LOG(ERROR) << "Failed to load Pepper module from " << path.value()
323 << " (error: " << error.ToString() << ")";
324 if (!base::PathExists(path)) {
325 ReportLoadResult(path, FILE_MISSING);
326 return;
328 ReportLoadResult(path, LOAD_FAILED);
329 // Report detailed reason for load failure.
330 ReportLoadErrorCode(path, error);
331 return;
334 // Only report load time for success loads.
335 ReportLoadTime(path, load_time);
337 // Get the GetInterface function (required).
338 plugin_entry_points_.get_interface =
339 reinterpret_cast<PP_GetInterface_Func>(
340 library.GetFunctionPointer("PPP_GetInterface"));
341 if (!plugin_entry_points_.get_interface) {
342 LOG(WARNING) << "No PPP_GetInterface in plugin library";
343 ReportLoadResult(path, ENTRY_POINT_MISSING);
344 return;
347 // The ShutdownModule/ShutdownBroker function is optional.
348 plugin_entry_points_.shutdown_module =
349 is_broker_ ?
350 reinterpret_cast<PP_ShutdownModule_Func>(
351 library.GetFunctionPointer("PPP_ShutdownBroker")) :
352 reinterpret_cast<PP_ShutdownModule_Func>(
353 library.GetFunctionPointer("PPP_ShutdownModule"));
355 if (!is_broker_) {
356 // Get the InitializeModule function (required for non-broker code).
357 plugin_entry_points_.initialize_module =
358 reinterpret_cast<PP_InitializeModule_Func>(
359 library.GetFunctionPointer("PPP_InitializeModule"));
360 if (!plugin_entry_points_.initialize_module) {
361 LOG(WARNING) << "No PPP_InitializeModule in plugin library";
362 ReportLoadResult(path, ENTRY_POINT_MISSING);
363 return;
368 #if defined(OS_WIN)
369 // If code subsequently tries to exit using abort(), force a crash (since
370 // otherwise these would be silent terminations and fly under the radar).
371 base::win::SetAbortBehaviorForCrashReporting();
373 // Once we lower the token the sandbox is locked down and no new modules
374 // can be loaded. TODO(cpu): consider changing to the loading style of
375 // regular plugins.
376 if (g_target_services) {
377 // Let Flash and Widevine CDM adapter load DXVA before lockdown on Vista+.
378 if (permissions.HasPermission(ppapi::PERMISSION_FLASH) ||
379 path.BaseName().MaybeAsASCII() == kWidevineCdmAdapterFileName) {
380 if (base::win::OSInfo::GetInstance()->version() >=
381 base::win::VERSION_VISTA) {
382 LoadLibraryA("dxva2.dll");
386 if (permissions.HasPermission(ppapi::PERMISSION_FLASH)) {
387 if (base::win::OSInfo::GetInstance()->version() >=
388 base::win::VERSION_WIN7) {
389 base::CPU cpu;
390 if (cpu.vendor_name() == "AuthenticAMD") {
391 // The AMD crypto acceleration is only AMD Bulldozer and above.
392 #if defined(_WIN64)
393 LoadLibraryA("amdhcp64.dll");
394 #else
395 LoadLibraryA("amdhcp32.dll");
396 #endif
401 // Cause advapi32 to load before the sandbox is turned on.
402 unsigned int dummy_rand;
403 rand_s(&dummy_rand);
405 WarmupWindowsLocales(permissions);
407 #if defined(ADDRESS_SANITIZER)
408 // Bind and leak dbghelp.dll before the token is lowered, otherwise
409 // AddressSanitizer will crash when trying to symbolize a report.
410 LoadLibraryA("dbghelp.dll");
411 #endif
413 g_target_services->LowerToken();
415 #endif
417 if (is_broker_) {
418 // Get the InitializeBroker function (required).
419 InitializeBrokerFunc init_broker =
420 reinterpret_cast<InitializeBrokerFunc>(
421 library.GetFunctionPointer("PPP_InitializeBroker"));
422 if (!init_broker) {
423 LOG(WARNING) << "No PPP_InitializeBroker in plugin library";
424 ReportLoadResult(path, ENTRY_POINT_MISSING);
425 return;
428 int32_t init_error = init_broker(&connect_instance_func_);
429 if (init_error != PP_OK) {
430 LOG(WARNING) << "InitBroker failed with error " << init_error;
431 ReportLoadResult(path, INIT_FAILED);
432 return;
434 if (!connect_instance_func_) {
435 LOG(WARNING) << "InitBroker did not provide PP_ConnectInstance_Func";
436 ReportLoadResult(path, INIT_FAILED);
437 return;
439 } else {
440 #if defined(OS_MACOSX)
441 // We need to do this after getting |PPP_GetInterface()| (or presumably
442 // doing something nontrivial with the library), else the sandbox
443 // intercedes.
444 CHECK(InitializeSandbox());
445 #endif
447 int32_t init_error = plugin_entry_points_.initialize_module(
448 local_pp_module_,
449 &ppapi::proxy::PluginDispatcher::GetBrowserInterface);
450 if (init_error != PP_OK) {
451 LOG(WARNING) << "InitModule failed with error " << init_error;
452 ReportLoadResult(path, INIT_FAILED);
453 return;
457 // Initialization succeeded, so keep the plugin DLL loaded.
458 library_.Reset(library.Release());
460 ReportLoadResult(path, LOAD_SUCCESS);
463 void PpapiThread::OnCreateChannel(base::ProcessId renderer_pid,
464 int renderer_child_id,
465 bool incognito) {
466 IPC::ChannelHandle channel_handle;
468 if (!plugin_entry_points_.get_interface || // Plugin couldn't be loaded.
469 !SetupRendererChannel(renderer_pid, renderer_child_id, incognito,
470 &channel_handle)) {
471 Send(new PpapiHostMsg_ChannelCreated(IPC::ChannelHandle()));
472 return;
475 Send(new PpapiHostMsg_ChannelCreated(channel_handle));
478 void PpapiThread::OnSetNetworkState(bool online) {
479 // Note the browser-process side shouldn't send us these messages in the
480 // first unless the plugin has dev permissions, so we don't need to check
481 // again here. We don't want random plugins depending on this dev interface.
482 if (!plugin_entry_points_.get_interface)
483 return;
484 const PPP_NetworkState_Dev* ns = static_cast<const PPP_NetworkState_Dev*>(
485 plugin_entry_points_.get_interface(PPP_NETWORK_STATE_DEV_INTERFACE));
486 if (ns)
487 ns->SetOnLine(PP_FromBool(online));
490 void PpapiThread::OnCrash() {
491 // Intentionally crash upon the request of the browser.
492 volatile int* null_pointer = NULL;
493 *null_pointer = 0;
496 void PpapiThread::OnHang() {
497 // Intentionally hang upon the request of the browser.
498 for (;;)
499 base::PlatformThread::Sleep(base::TimeDelta::FromSeconds(1));
502 bool PpapiThread::SetupRendererChannel(base::ProcessId renderer_pid,
503 int renderer_child_id,
504 bool incognito,
505 IPC::ChannelHandle* handle) {
506 DCHECK(is_broker_ == (connect_instance_func_ != NULL));
507 IPC::ChannelHandle plugin_handle;
508 plugin_handle.name = IPC::Channel::GenerateVerifiedChannelID(
509 base::StringPrintf(
510 "%d.r%d", base::GetCurrentProcId(), renderer_child_id));
512 ppapi::proxy::ProxyChannel* dispatcher = NULL;
513 bool init_result = false;
514 if (is_broker_) {
515 BrokerProcessDispatcher* broker_dispatcher =
516 new BrokerProcessDispatcher(plugin_entry_points_.get_interface,
517 connect_instance_func_);
518 init_result = broker_dispatcher->InitBrokerWithChannel(this,
519 renderer_pid,
520 plugin_handle,
521 false);
522 dispatcher = broker_dispatcher;
523 } else {
524 PluginProcessDispatcher* plugin_dispatcher =
525 new PluginProcessDispatcher(plugin_entry_points_.get_interface,
526 permissions_,
527 incognito);
528 init_result = plugin_dispatcher->InitPluginWithChannel(this,
529 renderer_pid,
530 plugin_handle,
531 false);
532 dispatcher = plugin_dispatcher;
535 if (!init_result) {
536 delete dispatcher;
537 return false;
540 handle->name = plugin_handle.name;
541 #if defined(OS_POSIX)
542 // On POSIX, transfer ownership of the renderer-side (client) FD.
543 // This ensures this process will be notified when it is closed even if a
544 // connection is not established.
545 handle->socket = base::FileDescriptor(dispatcher->TakeRendererFD());
546 if (handle->socket.fd == -1)
547 return false;
548 #endif
550 // From here, the dispatcher will manage its own lifetime according to the
551 // lifetime of the attached channel.
552 return true;
555 void PpapiThread::SavePluginName(const base::FilePath& path) {
556 ppapi::proxy::PluginGlobals::Get()->set_plugin_name(
557 path.BaseName().AsUTF8Unsafe());
559 // plugin() is NULL when in-process, which is fine, because this is
560 // just a hook for setting the process name.
561 if (GetContentClient()->plugin()) {
562 GetContentClient()->plugin()->PluginProcessStarted(
563 path.BaseName().RemoveExtension().LossyDisplayName());
567 static std::string GetHistogramName(bool is_broker,
568 const std::string& metric_name,
569 const base::FilePath& path) {
570 return std::string("Plugin.Ppapi") + (is_broker ? "Broker" : "Plugin") +
571 metric_name + "_" + path.BaseName().MaybeAsASCII();
574 void PpapiThread::ReportLoadResult(const base::FilePath& path,
575 LoadResult result) {
576 DCHECK_LT(result, LOAD_RESULT_MAX);
578 // Note: This leaks memory, which is expected behavior.
579 base::HistogramBase* histogram =
580 base::LinearHistogram::FactoryGet(
581 GetHistogramName(is_broker_, "LoadResult", path),
583 LOAD_RESULT_MAX,
584 LOAD_RESULT_MAX + 1,
585 base::HistogramBase::kUmaTargetedHistogramFlag);
587 histogram->Add(result);
590 void PpapiThread::ReportLoadErrorCode(
591 const base::FilePath& path,
592 const base::NativeLibraryLoadError& error) {
593 // Only report load error code on Windows because that's the only platform that
594 // has a numerical error value.
595 #if defined(OS_WIN)
596 // For sparse histograms, we can use the macro, as it does not incorporate a
597 // static.
598 UMA_HISTOGRAM_SPARSE_SLOWLY(
599 GetHistogramName(is_broker_, "LoadErrorCode", path), error.code);
600 #endif
603 void PpapiThread::ReportLoadTime(const base::FilePath& path,
604 const base::TimeDelta load_time) {
605 // Note: This leaks memory, which is expected behavior.
606 base::HistogramBase* histogram =
607 base::Histogram::FactoryTimeGet(
608 GetHistogramName(is_broker_, "LoadTime", path),
609 base::TimeDelta::FromMilliseconds(1),
610 base::TimeDelta::FromSeconds(10),
612 base::HistogramBase::kUmaTargetedHistogramFlag);
614 histogram->AddTime(load_time);
617 } // namespace content