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