Make sure webrtc::VideoSource is released when WebRtcVideoTrackAdapter is destroyed.
[chromium-blink-merge.git] / content / ppapi_plugin / ppapi_thread.cc
blobcb0abde2ba136aadc4e0afc70437bdd9feb2665a
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/logging.h"
13 #include "base/metrics/histogram.h"
14 #include "base/metrics/sparse_histogram.h"
15 #include "base/rand_util.h"
16 #include "base/strings/stringprintf.h"
17 #include "base/strings/utf_string_conversions.h"
18 #include "base/threading/platform_thread.h"
19 #include "base/time/time.h"
20 #include "content/child/browser_font_resource_trusted.h"
21 #include "content/child/child_process.h"
22 #include "content/common/child_process_messages.h"
23 #include "content/common/sandbox_util.h"
24 #include "content/ppapi_plugin/broker_process_dispatcher.h"
25 #include "content/ppapi_plugin/plugin_process_dispatcher.h"
26 #include "content/ppapi_plugin/ppapi_webkitplatformsupport_impl.h"
27 #include "content/public/common/content_client.h"
28 #include "content/public/common/content_switches.h"
29 #include "content/public/common/pepper_plugin_info.h"
30 #include "content/public/common/sandbox_init.h"
31 #include "content/public/plugin/content_plugin_client.h"
32 #include "ipc/ipc_channel_handle.h"
33 #include "ipc/ipc_platform_file.h"
34 #include "ipc/ipc_sync_channel.h"
35 #include "ipc/ipc_sync_message_filter.h"
36 #include "ppapi/c/dev/ppp_network_state_dev.h"
37 #include "ppapi/c/pp_errors.h"
38 #include "ppapi/c/ppp.h"
39 #include "ppapi/proxy/interface_list.h"
40 #include "ppapi/proxy/plugin_globals.h"
41 #include "ppapi/proxy/plugin_message_filter.h"
42 #include "ppapi/proxy/ppapi_messages.h"
43 #include "ppapi/proxy/resource_reply_thread_registrar.h"
44 #include "third_party/WebKit/public/web/WebKit.h"
45 #include "ui/base/ui_base_switches.h"
47 #if defined(OS_WIN)
48 #include "base/win/win_util.h"
49 #include "base/win/windows_version.h"
50 #include "sandbox/win/src/sandbox.h"
51 #elif defined(OS_MACOSX)
52 #include "content/common/sandbox_init_mac.h"
53 #endif
55 #if defined(OS_WIN)
56 extern sandbox::TargetServices* g_target_services;
58 // Used by EnumSystemLocales for warming up.
59 static BOOL CALLBACK EnumLocalesProc(LPTSTR lpLocaleString) {
60 return TRUE;
63 static BOOL CALLBACK EnumLocalesProcEx(
64 LPWSTR lpLocaleString,
65 DWORD dwFlags,
66 LPARAM lParam) {
67 return TRUE;
70 // Warm up language subsystems before the sandbox is turned on.
71 static void WarmupWindowsLocales(const ppapi::PpapiPermissions& permissions) {
72 ::GetUserDefaultLangID();
73 ::GetUserDefaultLCID();
75 if (permissions.HasPermission(ppapi::PERMISSION_FLASH)) {
76 if (base::win::GetVersion() >= base::win::VERSION_VISTA) {
77 typedef BOOL (WINAPI *PfnEnumSystemLocalesEx)
78 (LOCALE_ENUMPROCEX, DWORD, LPARAM, LPVOID);
80 HMODULE handle_kern32 = GetModuleHandleW(L"Kernel32.dll");
81 PfnEnumSystemLocalesEx enum_sys_locales_ex =
82 reinterpret_cast<PfnEnumSystemLocalesEx>
83 (GetProcAddress(handle_kern32, "EnumSystemLocalesEx"));
85 enum_sys_locales_ex(EnumLocalesProcEx, LOCALE_WINDOWS, 0, 0);
86 } else {
87 EnumSystemLocalesW(EnumLocalesProc, LCID_INSTALLED);
91 #else
92 extern void* g_target_services;
93 #endif
95 namespace content {
97 typedef int32_t (*InitializeBrokerFunc)
98 (PP_ConnectInstance_Func* connect_instance_func);
100 PpapiThread::PpapiThread(const CommandLine& command_line, bool is_broker)
101 : is_broker_(is_broker),
102 connect_instance_func_(NULL),
103 local_pp_module_(
104 base::RandInt(0, std::numeric_limits<PP_Module>::max())),
105 next_plugin_dispatcher_id_(1) {
106 ppapi::proxy::PluginGlobals* globals = ppapi::proxy::PluginGlobals::Get();
107 globals->set_plugin_proxy_delegate(this);
108 globals->set_command_line(
109 command_line.GetSwitchValueASCII(switches::kPpapiFlashArgs));
111 webkit_platform_support_.reset(new PpapiWebKitPlatformSupportImpl);
112 blink::initialize(webkit_platform_support_.get());
114 if (!is_broker_) {
115 channel()->AddFilter(
116 new ppapi::proxy::PluginMessageFilter(
117 NULL, globals->resource_reply_thread_registrar()));
121 PpapiThread::~PpapiThread() {
124 void PpapiThread::Shutdown() {
125 ppapi::proxy::PluginGlobals::Get()->set_plugin_proxy_delegate(NULL);
126 if (plugin_entry_points_.shutdown_module)
127 plugin_entry_points_.shutdown_module();
128 webkit_platform_support_->Shutdown();
129 blink::shutdown();
132 bool PpapiThread::Send(IPC::Message* msg) {
133 // Allow access from multiple threads.
134 if (base::MessageLoop::current() == message_loop())
135 return ChildThread::Send(msg);
137 return sync_message_filter()->Send(msg);
140 // Note that this function is called only for messages from the channel to the
141 // browser process. Messages from the renderer process are sent via a different
142 // channel that ends up at Dispatcher::OnMessageReceived.
143 bool PpapiThread::OnControlMessageReceived(const IPC::Message& msg) {
144 bool handled = true;
145 IPC_BEGIN_MESSAGE_MAP(PpapiThread, msg)
146 IPC_MESSAGE_HANDLER(PpapiMsg_LoadPlugin, OnLoadPlugin)
147 IPC_MESSAGE_HANDLER(PpapiMsg_CreateChannel, OnCreateChannel)
148 IPC_MESSAGE_HANDLER(PpapiMsg_SetNetworkState, OnSetNetworkState)
149 IPC_MESSAGE_HANDLER(PpapiMsg_Crash, OnCrash)
150 IPC_MESSAGE_HANDLER(PpapiMsg_Hang, OnHang)
151 IPC_MESSAGE_UNHANDLED(handled = false)
152 IPC_END_MESSAGE_MAP()
153 return handled;
156 void PpapiThread::OnChannelConnected(int32 peer_pid) {
157 ChildThread::OnChannelConnected(peer_pid);
158 #if defined(OS_WIN)
159 if (is_broker_)
160 peer_handle_.Set(::OpenProcess(PROCESS_DUP_HANDLE, FALSE, peer_pid));
161 #endif
164 base::MessageLoopProxy* PpapiThread::GetIPCMessageLoop() {
165 return ChildProcess::current()->io_message_loop_proxy();
168 base::WaitableEvent* PpapiThread::GetShutdownEvent() {
169 return ChildProcess::current()->GetShutDownEvent();
172 IPC::PlatformFileForTransit PpapiThread::ShareHandleWithRemote(
173 base::PlatformFile handle,
174 base::ProcessId peer_pid,
175 bool should_close_source) {
176 #if defined(OS_WIN)
177 if (peer_handle_.IsValid()) {
178 DCHECK(is_broker_);
179 return IPC::GetFileHandleForProcess(handle, peer_handle_,
180 should_close_source);
182 #endif
184 DCHECK(peer_pid != base::kNullProcessId);
185 return BrokerGetFileHandleForProcess(handle, peer_pid, should_close_source);
188 std::set<PP_Instance>* PpapiThread::GetGloballySeenInstanceIDSet() {
189 return &globally_seen_instance_ids_;
192 IPC::Sender* PpapiThread::GetBrowserSender() {
193 return this;
196 std::string PpapiThread::GetUILanguage() {
197 CommandLine* command_line = CommandLine::ForCurrentProcess();
198 return command_line->GetSwitchValueASCII(switches::kLang);
201 void PpapiThread::PreCacheFont(const void* logfontw) {
202 #if defined(OS_WIN)
203 Send(new ChildProcessHostMsg_PreCacheFont(
204 *static_cast<const LOGFONTW*>(logfontw)));
205 #endif
208 void PpapiThread::SetActiveURL(const std::string& url) {
209 GetContentClient()->SetActiveURL(GURL(url));
212 PP_Resource PpapiThread::CreateBrowserFont(
213 ppapi::proxy::Connection connection,
214 PP_Instance instance,
215 const PP_BrowserFont_Trusted_Description& desc,
216 const ppapi::Preferences& prefs) {
217 if (!BrowserFontResource_Trusted::IsPPFontDescriptionValid(desc))
218 return 0;
219 return (new BrowserFontResource_Trusted(
220 connection, instance, desc, prefs))->GetReference();
223 uint32 PpapiThread::Register(ppapi::proxy::PluginDispatcher* plugin_dispatcher) {
224 if (!plugin_dispatcher ||
225 plugin_dispatchers_.size() >= std::numeric_limits<uint32>::max()) {
226 return 0;
229 uint32 id = 0;
230 do {
231 // Although it is unlikely, make sure that we won't cause any trouble when
232 // the counter overflows.
233 id = next_plugin_dispatcher_id_++;
234 } while (id == 0 ||
235 plugin_dispatchers_.find(id) != plugin_dispatchers_.end());
236 plugin_dispatchers_[id] = plugin_dispatcher;
237 return id;
240 void PpapiThread::Unregister(uint32 plugin_dispatcher_id) {
241 plugin_dispatchers_.erase(plugin_dispatcher_id);
244 void PpapiThread::OnLoadPlugin(const base::FilePath& path,
245 const ppapi::PpapiPermissions& permissions) {
246 // In case of crashes, the crash dump doesn't indicate which plugin
247 // it came from.
248 base::debug::SetCrashKeyValue("ppapi_path", path.MaybeAsASCII());
250 SavePluginName(path);
252 // This must be set before calling into the plugin so it can get the
253 // interfaces it has permission for.
254 ppapi::proxy::InterfaceList::SetProcessGlobalPermissions(permissions);
255 permissions_ = permissions;
257 // Trusted Pepper plugins may be "internal", i.e. built-in to the browser
258 // binary. If we're being asked to load such a plugin (e.g. the Chromoting
259 // client) then fetch the entry points from the embedder, rather than a DLL.
260 std::vector<PepperPluginInfo> plugins;
261 GetContentClient()->AddPepperPlugins(&plugins);
262 for (size_t i = 0; i < plugins.size(); ++i) {
263 if (plugins[i].is_internal && plugins[i].path == path) {
264 // An internal plugin is being loaded, so fetch the entry points.
265 plugin_entry_points_ = plugins[i].internal_entry_points;
269 // If the plugin isn't internal then load it from |path|.
270 base::ScopedNativeLibrary library;
271 if (plugin_entry_points_.initialize_module == NULL) {
272 // Load the plugin from the specified library.
273 base::NativeLibraryLoadError error;
274 library.Reset(base::LoadNativeLibrary(path, &error));
275 if (!library.is_valid()) {
276 LOG(ERROR) << "Failed to load Pepper module from " << path.value()
277 << " (error: " << error.ToString() << ")";
278 ReportLoadResult(path, LOAD_FAILED);
279 // Report detailed reason for load failure.
280 ReportLoadErrorCode(path, error);
281 return;
284 // Get the GetInterface function (required).
285 plugin_entry_points_.get_interface =
286 reinterpret_cast<PP_GetInterface_Func>(
287 library.GetFunctionPointer("PPP_GetInterface"));
288 if (!plugin_entry_points_.get_interface) {
289 LOG(WARNING) << "No PPP_GetInterface in plugin library";
290 ReportLoadResult(path, ENTRY_POINT_MISSING);
291 return;
294 // The ShutdownModule/ShutdownBroker function is optional.
295 plugin_entry_points_.shutdown_module =
296 is_broker_ ?
297 reinterpret_cast<PP_ShutdownModule_Func>(
298 library.GetFunctionPointer("PPP_ShutdownBroker")) :
299 reinterpret_cast<PP_ShutdownModule_Func>(
300 library.GetFunctionPointer("PPP_ShutdownModule"));
302 if (!is_broker_) {
303 // Get the InitializeModule function (required for non-broker code).
304 plugin_entry_points_.initialize_module =
305 reinterpret_cast<PP_InitializeModule_Func>(
306 library.GetFunctionPointer("PPP_InitializeModule"));
307 if (!plugin_entry_points_.initialize_module) {
308 LOG(WARNING) << "No PPP_InitializeModule in plugin library";
309 ReportLoadResult(path, ENTRY_POINT_MISSING);
310 return;
315 #if defined(OS_WIN)
316 // If code subsequently tries to exit using abort(), force a crash (since
317 // otherwise these would be silent terminations and fly under the radar).
318 base::win::SetAbortBehaviorForCrashReporting();
320 // Once we lower the token the sandbox is locked down and no new modules
321 // can be loaded. TODO(cpu): consider changing to the loading style of
322 // regular plugins.
323 if (g_target_services) {
324 // Let Flash load DXVA before lockdown on Vista+.
325 if (permissions.HasPermission(ppapi::PERMISSION_FLASH)) {
326 if (base::win::OSInfo::GetInstance()->version() >=
327 base::win::VERSION_VISTA) {
328 LoadLibraryA("dxva2.dll");
331 if (base::win::OSInfo::GetInstance()->version() >=
332 base::win::VERSION_WIN7) {
333 base::CPU cpu;
334 if (cpu.vendor_name() == "AuthenticAMD") {
335 // The AMD crypto acceleration is only AMD Bulldozer and above.
336 #if defined(_WIN64)
337 LoadLibraryA("amdhcp64.dll");
338 #else
339 LoadLibraryA("amdhcp32.dll");
340 #endif
345 // Cause advapi32 to load before the sandbox is turned on.
346 unsigned int dummy_rand;
347 rand_s(&dummy_rand);
349 WarmupWindowsLocales(permissions);
351 g_target_services->LowerToken();
353 #endif
355 if (is_broker_) {
356 // Get the InitializeBroker function (required).
357 InitializeBrokerFunc init_broker =
358 reinterpret_cast<InitializeBrokerFunc>(
359 library.GetFunctionPointer("PPP_InitializeBroker"));
360 if (!init_broker) {
361 LOG(WARNING) << "No PPP_InitializeBroker in plugin library";
362 ReportLoadResult(path, ENTRY_POINT_MISSING);
363 return;
366 int32_t init_error = init_broker(&connect_instance_func_);
367 if (init_error != PP_OK) {
368 LOG(WARNING) << "InitBroker failed with error " << init_error;
369 ReportLoadResult(path, INIT_FAILED);
370 return;
372 if (!connect_instance_func_) {
373 LOG(WARNING) << "InitBroker did not provide PP_ConnectInstance_Func";
374 ReportLoadResult(path, INIT_FAILED);
375 return;
377 } else {
378 #if defined(OS_MACOSX)
379 // We need to do this after getting |PPP_GetInterface()| (or presumably
380 // doing something nontrivial with the library), else the sandbox
381 // intercedes.
382 CHECK(InitializeSandbox());
383 #endif
385 int32_t init_error = plugin_entry_points_.initialize_module(
386 local_pp_module_,
387 &ppapi::proxy::PluginDispatcher::GetBrowserInterface);
388 if (init_error != PP_OK) {
389 LOG(WARNING) << "InitModule failed with error " << init_error;
390 ReportLoadResult(path, INIT_FAILED);
391 return;
395 // Initialization succeeded, so keep the plugin DLL loaded.
396 library_.Reset(library.Release());
398 ReportLoadResult(path, LOAD_SUCCESS);
401 void PpapiThread::OnCreateChannel(base::ProcessId renderer_pid,
402 int renderer_child_id,
403 bool incognito) {
404 IPC::ChannelHandle channel_handle;
406 if (!plugin_entry_points_.get_interface || // Plugin couldn't be loaded.
407 !SetupRendererChannel(renderer_pid, renderer_child_id, incognito,
408 &channel_handle)) {
409 Send(new PpapiHostMsg_ChannelCreated(IPC::ChannelHandle()));
410 return;
413 Send(new PpapiHostMsg_ChannelCreated(channel_handle));
416 void PpapiThread::OnSetNetworkState(bool online) {
417 // Note the browser-process side shouldn't send us these messages in the
418 // first unless the plugin has dev permissions, so we don't need to check
419 // again here. We don't want random plugins depending on this dev interface.
420 if (!plugin_entry_points_.get_interface)
421 return;
422 const PPP_NetworkState_Dev* ns = static_cast<const PPP_NetworkState_Dev*>(
423 plugin_entry_points_.get_interface(PPP_NETWORK_STATE_DEV_INTERFACE));
424 if (ns)
425 ns->SetOnLine(PP_FromBool(online));
428 void PpapiThread::OnCrash() {
429 // Intentionally crash upon the request of the browser.
430 volatile int* null_pointer = NULL;
431 *null_pointer = 0;
434 void PpapiThread::OnHang() {
435 // Intentionally hang upon the request of the browser.
436 for (;;)
437 base::PlatformThread::Sleep(base::TimeDelta::FromSeconds(1));
440 bool PpapiThread::SetupRendererChannel(base::ProcessId renderer_pid,
441 int renderer_child_id,
442 bool incognito,
443 IPC::ChannelHandle* handle) {
444 DCHECK(is_broker_ == (connect_instance_func_ != NULL));
445 IPC::ChannelHandle plugin_handle;
446 plugin_handle.name = IPC::Channel::GenerateVerifiedChannelID(
447 base::StringPrintf(
448 "%d.r%d", base::GetCurrentProcId(), renderer_child_id));
450 ppapi::proxy::ProxyChannel* dispatcher = NULL;
451 bool init_result = false;
452 if (is_broker_) {
453 BrokerProcessDispatcher* broker_dispatcher =
454 new BrokerProcessDispatcher(plugin_entry_points_.get_interface,
455 connect_instance_func_);
456 init_result = broker_dispatcher->InitBrokerWithChannel(this,
457 renderer_pid,
458 plugin_handle,
459 false);
460 dispatcher = broker_dispatcher;
461 } else {
462 PluginProcessDispatcher* plugin_dispatcher =
463 new PluginProcessDispatcher(plugin_entry_points_.get_interface,
464 permissions_,
465 incognito);
466 init_result = plugin_dispatcher->InitPluginWithChannel(this,
467 renderer_pid,
468 plugin_handle,
469 false);
470 dispatcher = plugin_dispatcher;
473 if (!init_result) {
474 delete dispatcher;
475 return false;
478 handle->name = plugin_handle.name;
479 #if defined(OS_POSIX)
480 // On POSIX, transfer ownership of the renderer-side (client) FD.
481 // This ensures this process will be notified when it is closed even if a
482 // connection is not established.
483 handle->socket = base::FileDescriptor(dispatcher->TakeRendererFD(), true);
484 if (handle->socket.fd == -1)
485 return false;
486 #endif
488 // From here, the dispatcher will manage its own lifetime according to the
489 // lifetime of the attached channel.
490 return true;
493 void PpapiThread::SavePluginName(const base::FilePath& path) {
494 ppapi::proxy::PluginGlobals::Get()->set_plugin_name(
495 path.BaseName().AsUTF8Unsafe());
497 // plugin() is NULL when in-process, which is fine, because this is
498 // just a hook for setting the process name.
499 if (GetContentClient()->plugin()) {
500 GetContentClient()->plugin()->PluginProcessStarted(
501 path.BaseName().RemoveExtension().LossyDisplayName());
505 void PpapiThread::ReportLoadResult(const base::FilePath& path,
506 LoadResult result) {
507 DCHECK_LT(result, LOAD_RESULT_MAX);
508 std::string histogram_name = std::string("Plugin.Ppapi") +
509 (is_broker_ ? "Broker" : "Plugin") +
510 "LoadResult_" + path.BaseName().MaybeAsASCII();
512 // Note: This leaks memory, which is expected behavior.
513 base::HistogramBase* histogram =
514 base::LinearHistogram::FactoryGet(
515 histogram_name,
517 LOAD_RESULT_MAX,
518 LOAD_RESULT_MAX + 1,
519 base::HistogramBase::kUmaTargetedHistogramFlag);
521 histogram->Add(result);
524 void PpapiThread::ReportLoadErrorCode(
525 const base::FilePath& path,
526 const base::NativeLibraryLoadError& error) {
527 #if defined(OS_WIN)
528 // Only report load error code on Windows because that's the only platform
529 // that has a numerical error value.
530 std::string histogram_name =
531 std::string("Plugin.Ppapi") + (is_broker_ ? "Broker" : "Plugin") +
532 "LoadErrorCode_" + path.BaseName().MaybeAsASCII();
534 // For sparse histograms, we can use the macro, as it does not incorporate a
535 // static.
536 UMA_HISTOGRAM_SPARSE_SLOWLY(histogram_name, error.code);
537 #endif
540 } // namespace content