FamilyInfoFetcher: Use kid.family.readonly scope instead of kid.family
[chromium-blink-merge.git] / content / ppapi_plugin / ppapi_thread.cc
blob88b824f41b3f7bec485eb06580023526a977220a
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/metrics/histogram.h"
15 #include "base/metrics/sparse_histogram.h"
16 #include "base/rand_util.h"
17 #include "base/strings/stringprintf.h"
18 #include "base/strings/utf_string_conversions.h"
19 #include "base/threading/platform_thread.h"
20 #include "base/time/time.h"
21 #include "content/child/browser_font_resource_trusted.h"
22 #include "content/child/child_process.h"
23 #include "content/common/child_process_messages.h"
24 #include "content/common/sandbox_util.h"
25 #include "content/ppapi_plugin/broker_process_dispatcher.h"
26 #include "content/ppapi_plugin/plugin_process_dispatcher.h"
27 #include "content/ppapi_plugin/ppapi_blink_platform_impl.h"
28 #include "content/public/common/content_client.h"
29 #include "content/public/common/content_switches.h"
30 #include "content/public/common/pepper_plugin_info.h"
31 #include "content/public/common/sandbox_init.h"
32 #include "content/public/plugin/content_plugin_client.h"
33 #include "ipc/ipc_channel_handle.h"
34 #include "ipc/ipc_platform_file.h"
35 #include "ipc/ipc_sync_channel.h"
36 #include "ipc/ipc_sync_message_filter.h"
37 #include "ppapi/c/dev/ppp_network_state_dev.h"
38 #include "ppapi/c/pp_errors.h"
39 #include "ppapi/c/ppp.h"
40 #include "ppapi/proxy/interface_list.h"
41 #include "ppapi/proxy/plugin_globals.h"
42 #include "ppapi/proxy/plugin_message_filter.h"
43 #include "ppapi/proxy/ppapi_messages.h"
44 #include "ppapi/proxy/resource_reply_thread_registrar.h"
45 #include "third_party/WebKit/public/web/WebKit.h"
46 #include "ui/base/ui_base_switches.h"
48 #if defined(OS_WIN)
49 #include "base/win/win_util.h"
50 #include "base/win/windows_version.h"
51 #include "sandbox/win/src/sandbox.h"
52 #elif defined(OS_MACOSX)
53 #include "content/common/sandbox_init_mac.h"
54 #endif
56 #if defined(OS_WIN)
57 const char kWidevineCdmAdapterFileName[] = "widevinecdmadapter.dll";
59 extern sandbox::TargetServices* g_target_services;
61 // Used by EnumSystemLocales for warming up.
62 static BOOL CALLBACK EnumLocalesProc(LPTSTR lpLocaleString) {
63 return TRUE;
66 static BOOL CALLBACK EnumLocalesProcEx(
67 LPWSTR lpLocaleString,
68 DWORD dwFlags,
69 LPARAM lParam) {
70 return TRUE;
73 // Warm up language subsystems before the sandbox is turned on.
74 static void WarmupWindowsLocales(const ppapi::PpapiPermissions& permissions) {
75 ::GetUserDefaultLangID();
76 ::GetUserDefaultLCID();
78 if (permissions.HasPermission(ppapi::PERMISSION_FLASH)) {
79 if (base::win::GetVersion() >= base::win::VERSION_VISTA) {
80 typedef BOOL (WINAPI *PfnEnumSystemLocalesEx)
81 (LOCALE_ENUMPROCEX, DWORD, LPARAM, LPVOID);
83 HMODULE handle_kern32 = GetModuleHandleW(L"Kernel32.dll");
84 PfnEnumSystemLocalesEx enum_sys_locales_ex =
85 reinterpret_cast<PfnEnumSystemLocalesEx>
86 (GetProcAddress(handle_kern32, "EnumSystemLocalesEx"));
88 enum_sys_locales_ex(EnumLocalesProcEx, LOCALE_WINDOWS, 0, 0);
89 } else {
90 EnumSystemLocalesW(EnumLocalesProc, LCID_INSTALLED);
95 #endif
97 namespace content {
99 typedef int32_t (*InitializeBrokerFunc)
100 (PP_ConnectInstance_Func* connect_instance_func);
102 PpapiThread::PpapiThread(const base::CommandLine& command_line, bool is_broker)
103 : is_broker_(is_broker),
104 plugin_globals_(GetIOTaskRunner()),
105 connect_instance_func_(NULL),
106 local_pp_module_(base::RandInt(0, std::numeric_limits<PP_Module>::max())),
107 next_plugin_dispatcher_id_(1) {
108 plugin_globals_.SetPluginProxyDelegate(this);
109 plugin_globals_.set_command_line(
110 command_line.GetSwitchValueASCII(switches::kPpapiFlashArgs));
112 blink_platform_impl_.reset(new PpapiBlinkPlatformImpl);
113 blink::initialize(blink_platform_impl_.get());
115 if (!is_broker_) {
116 scoped_refptr<ppapi::proxy::PluginMessageFilter> plugin_filter(
117 new ppapi::proxy::PluginMessageFilter(
118 NULL, plugin_globals_.resource_reply_thread_registrar()));
119 channel()->AddFilter(plugin_filter.get());
120 plugin_globals_.RegisterResourceMessageFilters(plugin_filter.get());
124 PpapiThread::~PpapiThread() {
127 void PpapiThread::Shutdown() {
128 ChildThreadImpl::Shutdown();
130 ppapi::proxy::PluginGlobals::Get()->ResetPluginProxyDelegate();
131 if (plugin_entry_points_.shutdown_module)
132 plugin_entry_points_.shutdown_module();
133 blink_platform_impl_->Shutdown();
134 blink::shutdown();
137 bool PpapiThread::Send(IPC::Message* msg) {
138 // Allow access from multiple threads.
139 if (base::MessageLoop::current() == message_loop())
140 return ChildThreadImpl::Send(msg);
142 return sync_message_filter()->Send(msg);
145 // Note that this function is called only for messages from the channel to the
146 // browser process. Messages from the renderer process are sent via a different
147 // channel that ends up at Dispatcher::OnMessageReceived.
148 bool PpapiThread::OnControlMessageReceived(const IPC::Message& msg) {
149 bool handled = true;
150 IPC_BEGIN_MESSAGE_MAP(PpapiThread, msg)
151 IPC_MESSAGE_HANDLER(PpapiMsg_LoadPlugin, OnLoadPlugin)
152 IPC_MESSAGE_HANDLER(PpapiMsg_CreateChannel, OnCreateChannel)
153 IPC_MESSAGE_HANDLER(PpapiMsg_SetNetworkState, OnSetNetworkState)
154 IPC_MESSAGE_HANDLER(PpapiMsg_Crash, OnCrash)
155 IPC_MESSAGE_HANDLER(PpapiMsg_Hang, OnHang)
156 IPC_MESSAGE_UNHANDLED(handled = false)
157 IPC_END_MESSAGE_MAP()
158 return handled;
161 void PpapiThread::OnChannelConnected(int32 peer_pid) {
162 ChildThreadImpl::OnChannelConnected(peer_pid);
163 #if defined(OS_WIN)
164 if (is_broker_)
165 peer_handle_.Set(::OpenProcess(PROCESS_DUP_HANDLE, FALSE, peer_pid));
166 #endif
169 base::MessageLoopProxy* PpapiThread::GetIPCMessageLoop() {
170 return ChildProcess::current()->io_message_loop_proxy();
173 base::WaitableEvent* PpapiThread::GetShutdownEvent() {
174 return ChildProcess::current()->GetShutDownEvent();
177 IPC::PlatformFileForTransit PpapiThread::ShareHandleWithRemote(
178 base::PlatformFile handle,
179 base::ProcessId peer_pid,
180 bool should_close_source) {
181 #if defined(OS_WIN)
182 if (peer_handle_.IsValid()) {
183 DCHECK(is_broker_);
184 return IPC::GetFileHandleForProcess(handle, peer_handle_.Get(),
185 should_close_source);
187 #endif
189 DCHECK(peer_pid != base::kNullProcessId);
190 return BrokerGetFileHandleForProcess(handle, peer_pid, should_close_source);
193 std::set<PP_Instance>* PpapiThread::GetGloballySeenInstanceIDSet() {
194 return &globally_seen_instance_ids_;
197 IPC::Sender* PpapiThread::GetBrowserSender() {
198 return this;
201 std::string PpapiThread::GetUILanguage() {
202 base::CommandLine* command_line = base::CommandLine::ForCurrentProcess();
203 return command_line->GetSwitchValueASCII(switches::kLang);
206 void PpapiThread::PreCacheFont(const void* logfontw) {
207 #if defined(OS_WIN)
208 ChildThreadImpl::PreCacheFont(*static_cast<const LOGFONTW*>(logfontw));
209 #endif
212 void PpapiThread::SetActiveURL(const std::string& url) {
213 GetContentClient()->SetActiveURL(GURL(url));
216 PP_Resource PpapiThread::CreateBrowserFont(
217 ppapi::proxy::Connection connection,
218 PP_Instance instance,
219 const PP_BrowserFont_Trusted_Description& desc,
220 const ppapi::Preferences& prefs) {
221 if (!BrowserFontResource_Trusted::IsPPFontDescriptionValid(desc))
222 return 0;
223 return (new BrowserFontResource_Trusted(
224 connection, instance, desc, prefs))->GetReference();
227 uint32 PpapiThread::Register(ppapi::proxy::PluginDispatcher* plugin_dispatcher) {
228 if (!plugin_dispatcher ||
229 plugin_dispatchers_.size() >= std::numeric_limits<uint32>::max()) {
230 return 0;
233 uint32 id = 0;
234 do {
235 // Although it is unlikely, make sure that we won't cause any trouble when
236 // the counter overflows.
237 id = next_plugin_dispatcher_id_++;
238 } while (id == 0 ||
239 plugin_dispatchers_.find(id) != plugin_dispatchers_.end());
240 plugin_dispatchers_[id] = plugin_dispatcher;
241 return id;
244 void PpapiThread::Unregister(uint32 plugin_dispatcher_id) {
245 plugin_dispatchers_.erase(plugin_dispatcher_id);
248 void PpapiThread::OnLoadPlugin(const base::FilePath& path,
249 const ppapi::PpapiPermissions& permissions) {
250 // In case of crashes, the crash dump doesn't indicate which plugin
251 // it came from.
252 base::debug::SetCrashKeyValue("ppapi_path", path.MaybeAsASCII());
254 SavePluginName(path);
256 // This must be set before calling into the plugin so it can get the
257 // interfaces it has permission for.
258 ppapi::proxy::InterfaceList::SetProcessGlobalPermissions(permissions);
259 permissions_ = permissions;
261 // Trusted Pepper plugins may be "internal", i.e. built-in to the browser
262 // binary. If we're being asked to load such a plugin (e.g. the Chromoting
263 // client) then fetch the entry points from the embedder, rather than a DLL.
264 std::vector<PepperPluginInfo> plugins;
265 GetContentClient()->AddPepperPlugins(&plugins);
266 for (size_t i = 0; i < plugins.size(); ++i) {
267 if (plugins[i].is_internal && plugins[i].path == path) {
268 // An internal plugin is being loaded, so fetch the entry points.
269 plugin_entry_points_ = plugins[i].internal_entry_points;
273 // If the plugin isn't internal then load it from |path|.
274 base::ScopedNativeLibrary library;
275 if (plugin_entry_points_.initialize_module == NULL) {
276 // Load the plugin from the specified library.
277 base::NativeLibraryLoadError error;
278 library.Reset(base::LoadNativeLibrary(path, &error));
279 if (!library.is_valid()) {
280 LOG(ERROR) << "Failed to load Pepper module from " << path.value()
281 << " (error: " << error.ToString() << ")";
282 if (!base::PathExists(path)) {
283 ReportLoadResult(path, FILE_MISSING);
284 return;
286 ReportLoadResult(path, LOAD_FAILED);
287 // Report detailed reason for load failure.
288 ReportLoadErrorCode(path, error);
289 return;
292 // Get the GetInterface function (required).
293 plugin_entry_points_.get_interface =
294 reinterpret_cast<PP_GetInterface_Func>(
295 library.GetFunctionPointer("PPP_GetInterface"));
296 if (!plugin_entry_points_.get_interface) {
297 LOG(WARNING) << "No PPP_GetInterface in plugin library";
298 ReportLoadResult(path, ENTRY_POINT_MISSING);
299 return;
302 // The ShutdownModule/ShutdownBroker function is optional.
303 plugin_entry_points_.shutdown_module =
304 is_broker_ ?
305 reinterpret_cast<PP_ShutdownModule_Func>(
306 library.GetFunctionPointer("PPP_ShutdownBroker")) :
307 reinterpret_cast<PP_ShutdownModule_Func>(
308 library.GetFunctionPointer("PPP_ShutdownModule"));
310 if (!is_broker_) {
311 // Get the InitializeModule function (required for non-broker code).
312 plugin_entry_points_.initialize_module =
313 reinterpret_cast<PP_InitializeModule_Func>(
314 library.GetFunctionPointer("PPP_InitializeModule"));
315 if (!plugin_entry_points_.initialize_module) {
316 LOG(WARNING) << "No PPP_InitializeModule in plugin library";
317 ReportLoadResult(path, ENTRY_POINT_MISSING);
318 return;
323 #if defined(OS_WIN)
324 // If code subsequently tries to exit using abort(), force a crash (since
325 // otherwise these would be silent terminations and fly under the radar).
326 base::win::SetAbortBehaviorForCrashReporting();
328 // Once we lower the token the sandbox is locked down and no new modules
329 // can be loaded. TODO(cpu): consider changing to the loading style of
330 // regular plugins.
331 if (g_target_services) {
332 // Let Flash and Widevine CDM adapter load DXVA before lockdown on Vista+.
333 if (permissions.HasPermission(ppapi::PERMISSION_FLASH) ||
334 path.BaseName().MaybeAsASCII() == kWidevineCdmAdapterFileName) {
335 if (base::win::OSInfo::GetInstance()->version() >=
336 base::win::VERSION_VISTA) {
337 LoadLibraryA("dxva2.dll");
341 if (permissions.HasPermission(ppapi::PERMISSION_FLASH)) {
342 if (base::win::OSInfo::GetInstance()->version() >=
343 base::win::VERSION_WIN7) {
344 base::CPU cpu;
345 if (cpu.vendor_name() == "AuthenticAMD") {
346 // The AMD crypto acceleration is only AMD Bulldozer and above.
347 #if defined(_WIN64)
348 LoadLibraryA("amdhcp64.dll");
349 #else
350 LoadLibraryA("amdhcp32.dll");
351 #endif
356 // Cause advapi32 to load before the sandbox is turned on.
357 unsigned int dummy_rand;
358 rand_s(&dummy_rand);
360 WarmupWindowsLocales(permissions);
362 #if defined(ADDRESS_SANITIZER)
363 // Bind and leak dbghelp.dll before the token is lowered, otherwise
364 // AddressSanitizer will crash when trying to symbolize a report.
365 LoadLibraryA("dbghelp.dll");
366 #endif
368 g_target_services->LowerToken();
370 #endif
372 if (is_broker_) {
373 // Get the InitializeBroker function (required).
374 InitializeBrokerFunc init_broker =
375 reinterpret_cast<InitializeBrokerFunc>(
376 library.GetFunctionPointer("PPP_InitializeBroker"));
377 if (!init_broker) {
378 LOG(WARNING) << "No PPP_InitializeBroker in plugin library";
379 ReportLoadResult(path, ENTRY_POINT_MISSING);
380 return;
383 int32_t init_error = init_broker(&connect_instance_func_);
384 if (init_error != PP_OK) {
385 LOG(WARNING) << "InitBroker failed with error " << init_error;
386 ReportLoadResult(path, INIT_FAILED);
387 return;
389 if (!connect_instance_func_) {
390 LOG(WARNING) << "InitBroker did not provide PP_ConnectInstance_Func";
391 ReportLoadResult(path, INIT_FAILED);
392 return;
394 } else {
395 #if defined(OS_MACOSX)
396 // We need to do this after getting |PPP_GetInterface()| (or presumably
397 // doing something nontrivial with the library), else the sandbox
398 // intercedes.
399 CHECK(InitializeSandbox());
400 #endif
402 int32_t init_error = plugin_entry_points_.initialize_module(
403 local_pp_module_,
404 &ppapi::proxy::PluginDispatcher::GetBrowserInterface);
405 if (init_error != PP_OK) {
406 LOG(WARNING) << "InitModule failed with error " << init_error;
407 ReportLoadResult(path, INIT_FAILED);
408 return;
412 // Initialization succeeded, so keep the plugin DLL loaded.
413 library_.Reset(library.Release());
415 ReportLoadResult(path, LOAD_SUCCESS);
418 void PpapiThread::OnCreateChannel(base::ProcessId renderer_pid,
419 int renderer_child_id,
420 bool incognito) {
421 IPC::ChannelHandle channel_handle;
423 if (!plugin_entry_points_.get_interface || // Plugin couldn't be loaded.
424 !SetupRendererChannel(renderer_pid, renderer_child_id, incognito,
425 &channel_handle)) {
426 Send(new PpapiHostMsg_ChannelCreated(IPC::ChannelHandle()));
427 return;
430 Send(new PpapiHostMsg_ChannelCreated(channel_handle));
433 void PpapiThread::OnSetNetworkState(bool online) {
434 // Note the browser-process side shouldn't send us these messages in the
435 // first unless the plugin has dev permissions, so we don't need to check
436 // again here. We don't want random plugins depending on this dev interface.
437 if (!plugin_entry_points_.get_interface)
438 return;
439 const PPP_NetworkState_Dev* ns = static_cast<const PPP_NetworkState_Dev*>(
440 plugin_entry_points_.get_interface(PPP_NETWORK_STATE_DEV_INTERFACE));
441 if (ns)
442 ns->SetOnLine(PP_FromBool(online));
445 void PpapiThread::OnCrash() {
446 // Intentionally crash upon the request of the browser.
447 volatile int* null_pointer = NULL;
448 *null_pointer = 0;
451 void PpapiThread::OnHang() {
452 // Intentionally hang upon the request of the browser.
453 for (;;)
454 base::PlatformThread::Sleep(base::TimeDelta::FromSeconds(1));
457 bool PpapiThread::SetupRendererChannel(base::ProcessId renderer_pid,
458 int renderer_child_id,
459 bool incognito,
460 IPC::ChannelHandle* handle) {
461 DCHECK(is_broker_ == (connect_instance_func_ != NULL));
462 IPC::ChannelHandle plugin_handle;
463 plugin_handle.name = IPC::Channel::GenerateVerifiedChannelID(
464 base::StringPrintf(
465 "%d.r%d", base::GetCurrentProcId(), renderer_child_id));
467 ppapi::proxy::ProxyChannel* dispatcher = NULL;
468 bool init_result = false;
469 if (is_broker_) {
470 BrokerProcessDispatcher* broker_dispatcher =
471 new BrokerProcessDispatcher(plugin_entry_points_.get_interface,
472 connect_instance_func_);
473 init_result = broker_dispatcher->InitBrokerWithChannel(this,
474 renderer_pid,
475 plugin_handle,
476 false);
477 dispatcher = broker_dispatcher;
478 } else {
479 PluginProcessDispatcher* plugin_dispatcher =
480 new PluginProcessDispatcher(plugin_entry_points_.get_interface,
481 permissions_,
482 incognito);
483 init_result = plugin_dispatcher->InitPluginWithChannel(this,
484 renderer_pid,
485 plugin_handle,
486 false);
487 dispatcher = plugin_dispatcher;
490 if (!init_result) {
491 delete dispatcher;
492 return false;
495 handle->name = plugin_handle.name;
496 #if defined(OS_POSIX)
497 // On POSIX, transfer ownership of the renderer-side (client) FD.
498 // This ensures this process will be notified when it is closed even if a
499 // connection is not established.
500 handle->socket = base::FileDescriptor(dispatcher->TakeRendererFD());
501 if (handle->socket.fd == -1)
502 return false;
503 #endif
505 // From here, the dispatcher will manage its own lifetime according to the
506 // lifetime of the attached channel.
507 return true;
510 void PpapiThread::SavePluginName(const base::FilePath& path) {
511 ppapi::proxy::PluginGlobals::Get()->set_plugin_name(
512 path.BaseName().AsUTF8Unsafe());
514 // plugin() is NULL when in-process, which is fine, because this is
515 // just a hook for setting the process name.
516 if (GetContentClient()->plugin()) {
517 GetContentClient()->plugin()->PluginProcessStarted(
518 path.BaseName().RemoveExtension().LossyDisplayName());
522 void PpapiThread::ReportLoadResult(const base::FilePath& path,
523 LoadResult result) {
524 DCHECK_LT(result, LOAD_RESULT_MAX);
525 std::string histogram_name = std::string("Plugin.Ppapi") +
526 (is_broker_ ? "Broker" : "Plugin") +
527 "LoadResult_" + path.BaseName().MaybeAsASCII();
529 // Note: This leaks memory, which is expected behavior.
530 base::HistogramBase* histogram =
531 base::LinearHistogram::FactoryGet(
532 histogram_name,
534 LOAD_RESULT_MAX,
535 LOAD_RESULT_MAX + 1,
536 base::HistogramBase::kUmaTargetedHistogramFlag);
538 histogram->Add(result);
541 void PpapiThread::ReportLoadErrorCode(
542 const base::FilePath& path,
543 const base::NativeLibraryLoadError& error) {
544 #if defined(OS_WIN)
545 // Only report load error code on Windows because that's the only platform
546 // that has a numerical error value.
547 std::string histogram_name =
548 std::string("Plugin.Ppapi") + (is_broker_ ? "Broker" : "Plugin") +
549 "LoadErrorCode_" + path.BaseName().MaybeAsASCII();
551 // For sparse histograms, we can use the macro, as it does not incorporate a
552 // static.
553 UMA_HISTOGRAM_SPARSE_SLOWLY(histogram_name, error.code);
554 #endif
557 } // namespace content