SimpleCache: add an histogram to record the age of entry on open
[chromium-blink-merge.git] / content / ppapi_plugin / ppapi_thread.cc
blob73d51ad7c3193f87d1ba940b685765277fb70380
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/rand_util.h"
14 #include "base/strings/stringprintf.h"
15 #include "base/strings/utf_string_conversions.h"
16 #include "base/threading/platform_thread.h"
17 #include "base/time/time.h"
18 #include "content/child/browser_font_resource_trusted.h"
19 #include "content/child/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/content_switches.h"
27 #include "content/public/common/pepper_plugin_info.h"
28 #include "content/public/common/sandbox_init.h"
29 #include "content/public/plugin/content_plugin_client.h"
30 #include "ipc/ipc_channel_handle.h"
31 #include "ipc/ipc_platform_file.h"
32 #include "ipc/ipc_sync_channel.h"
33 #include "ipc/ipc_sync_message_filter.h"
34 #include "ppapi/c/dev/ppp_network_state_dev.h"
35 #include "ppapi/c/pp_errors.h"
36 #include "ppapi/c/ppp.h"
37 #include "ppapi/proxy/interface_list.h"
38 #include "ppapi/proxy/plugin_globals.h"
39 #include "ppapi/proxy/ppapi_messages.h"
40 #include "ppapi/shared_impl/api_id.h"
41 #include "third_party/WebKit/public/web/WebKit.h"
42 #include "ui/base/ui_base_switches.h"
44 #if defined(OS_WIN)
45 #include "base/win/win_util.h"
46 #include "base/win/windows_version.h"
47 #include "sandbox/win/src/sandbox.h"
48 #elif defined(OS_MACOSX)
49 #include "content/common/sandbox_init_mac.h"
50 #endif
52 #if defined(OS_WIN)
53 extern sandbox::TargetServices* g_target_services;
54 #else
55 extern void* g_target_services;
56 #endif
58 namespace content {
60 typedef int32_t (*InitializeBrokerFunc)
61 (PP_ConnectInstance_Func* connect_instance_func);
63 PpapiThread::DispatcherMessageListener::DispatcherMessageListener(
64 PpapiThread* owner) : owner_(owner) {
67 PpapiThread::DispatcherMessageListener::~DispatcherMessageListener() {
70 bool PpapiThread::DispatcherMessageListener::OnMessageReceived(
71 const IPC::Message& msg) {
72 // The first parameter should be a plugin dispatcher ID.
73 PickleIterator iter(msg);
74 uint32 id = 0;
75 if (!msg.ReadUInt32(&iter, &id)) {
76 NOTREACHED();
77 return false;
79 std::map<uint32, ppapi::proxy::PluginDispatcher*>::iterator dispatcher =
80 owner_->plugin_dispatchers_.find(id);
81 if (dispatcher != owner_->plugin_dispatchers_.end())
82 return dispatcher->second->OnMessageReceived(msg);
84 return false;
87 PpapiThread::PpapiThread(const CommandLine& command_line, bool is_broker)
88 : is_broker_(is_broker),
89 connect_instance_func_(NULL),
90 local_pp_module_(
91 base::RandInt(0, std::numeric_limits<PP_Module>::max())),
92 next_plugin_dispatcher_id_(1),
93 dispatcher_message_listener_(this) {
94 ppapi::proxy::PluginGlobals* globals = ppapi::proxy::PluginGlobals::Get();
95 globals->set_plugin_proxy_delegate(this);
96 globals->set_command_line(
97 command_line.GetSwitchValueASCII(switches::kPpapiFlashArgs));
99 webkit_platform_support_.reset(new PpapiWebKitPlatformSupportImpl);
100 WebKit::initialize(webkit_platform_support_.get());
102 // Register interfaces that expect messages from the browser process. Please
103 // note that only those InterfaceProxy-based ones require registration.
104 AddRoute(ppapi::API_ID_PPB_TCPSOCKET,
105 &dispatcher_message_listener_);
106 AddRoute(ppapi::API_ID_PPB_TCPSOCKET_PRIVATE,
107 &dispatcher_message_listener_);
108 AddRoute(ppapi::API_ID_PPB_HOSTRESOLVER_PRIVATE,
109 &dispatcher_message_listener_);
110 AddRoute(ppapi::API_ID_PPB_NETWORKMANAGER_PRIVATE,
111 &dispatcher_message_listener_);
114 PpapiThread::~PpapiThread() {
117 void PpapiThread::Shutdown() {
118 ppapi::proxy::PluginGlobals::Get()->set_plugin_proxy_delegate(NULL);
119 if (plugin_entry_points_.shutdown_module)
120 plugin_entry_points_.shutdown_module();
121 WebKit::shutdown();
123 #if defined(OS_WIN)
124 if (permissions_.HasPermission(ppapi::PERMISSION_FLASH))
125 base::win::SetShouldCrashOnProcessDetach(false);
126 #endif
129 bool PpapiThread::Send(IPC::Message* msg) {
130 // Allow access from multiple threads.
131 if (base::MessageLoop::current() == message_loop())
132 return ChildThread::Send(msg);
134 return sync_message_filter()->Send(msg);
137 // Note that this function is called only for messages from the channel to the
138 // browser process. Messages from the renderer process are sent via a different
139 // channel that ends up at Dispatcher::OnMessageReceived.
140 bool PpapiThread::OnControlMessageReceived(const IPC::Message& msg) {
141 bool handled = true;
142 IPC_BEGIN_MESSAGE_MAP(PpapiThread, msg)
143 IPC_MESSAGE_HANDLER(PpapiMsg_LoadPlugin, OnLoadPlugin)
144 IPC_MESSAGE_HANDLER(PpapiMsg_CreateChannel, OnCreateChannel)
145 IPC_MESSAGE_HANDLER(PpapiMsg_SetNetworkState, OnSetNetworkState)
146 IPC_MESSAGE_HANDLER(PpapiMsg_Crash, OnCrash)
147 IPC_MESSAGE_HANDLER(PpapiMsg_Hang, OnHang)
148 IPC_MESSAGE_HANDLER(PpapiPluginMsg_ResourceReply, OnResourceReply)
149 IPC_MESSAGE_UNHANDLED(handled = false)
150 IPC_END_MESSAGE_MAP()
151 return handled;
154 void PpapiThread::OnChannelConnected(int32 peer_pid) {
155 ChildThread::OnChannelConnected(peer_pid);
156 #if defined(OS_WIN)
157 if (is_broker_)
158 peer_handle_.Set(::OpenProcess(PROCESS_DUP_HANDLE, FALSE, peer_pid));
159 #endif
162 base::MessageLoopProxy* PpapiThread::GetIPCMessageLoop() {
163 return ChildProcess::current()->io_message_loop_proxy();
166 base::WaitableEvent* PpapiThread::GetShutdownEvent() {
167 return ChildProcess::current()->GetShutDownEvent();
170 IPC::PlatformFileForTransit PpapiThread::ShareHandleWithRemote(
171 base::PlatformFile handle,
172 base::ProcessId peer_pid,
173 bool should_close_source) {
174 #if defined(OS_WIN)
175 if (peer_handle_.IsValid()) {
176 DCHECK(is_broker_);
177 return IPC::GetFileHandleForProcess(handle, peer_handle_,
178 should_close_source);
180 #endif
182 DCHECK(peer_pid != base::kNullProcessId);
183 return BrokerGetFileHandleForProcess(handle, peer_pid, should_close_source);
186 std::set<PP_Instance>* PpapiThread::GetGloballySeenInstanceIDSet() {
187 return &globally_seen_instance_ids_;
190 IPC::Sender* PpapiThread::GetBrowserSender() {
191 return this;
194 std::string PpapiThread::GetUILanguage() {
195 CommandLine* command_line = CommandLine::ForCurrentProcess();
196 return command_line->GetSwitchValueASCII(switches::kLang);
199 void PpapiThread::PreCacheFont(const void* logfontw) {
200 #if defined(OS_WIN)
201 Send(new ChildProcessHostMsg_PreCacheFont(
202 *static_cast<const LOGFONTW*>(logfontw)));
203 #endif
206 void PpapiThread::SetActiveURL(const std::string& url) {
207 GetContentClient()->SetActiveURL(GURL(url));
210 PP_Resource PpapiThread::CreateBrowserFont(
211 ppapi::proxy::Connection connection,
212 PP_Instance instance,
213 const PP_BrowserFont_Trusted_Description& desc,
214 const ppapi::Preferences& prefs) {
215 if (!BrowserFontResource_Trusted::IsPPFontDescriptionValid(desc))
216 return 0;
217 return (new BrowserFontResource_Trusted(
218 connection, instance, desc, prefs))->GetReference();
221 uint32 PpapiThread::Register(ppapi::proxy::PluginDispatcher* plugin_dispatcher) {
222 if (!plugin_dispatcher ||
223 plugin_dispatchers_.size() >= std::numeric_limits<uint32>::max()) {
224 return 0;
227 uint32 id = 0;
228 do {
229 // Although it is unlikely, make sure that we won't cause any trouble when
230 // the counter overflows.
231 id = next_plugin_dispatcher_id_++;
232 } while (id == 0 ||
233 plugin_dispatchers_.find(id) != plugin_dispatchers_.end());
234 plugin_dispatchers_[id] = plugin_dispatcher;
235 return id;
238 void PpapiThread::Unregister(uint32 plugin_dispatcher_id) {
239 plugin_dispatchers_.erase(plugin_dispatcher_id);
242 void PpapiThread::OnLoadPlugin(const base::FilePath& path,
243 const ppapi::PpapiPermissions& permissions) {
244 // In case of crashes, the crash dump doesn't indicate which plugin
245 // it came from.
246 base::debug::SetCrashKeyValue("ppapi_path", path.MaybeAsASCII());
248 SavePluginName(path);
250 // This must be set before calling into the plugin so it can get the
251 // interfaces it has permission for.
252 ppapi::proxy::InterfaceList::SetProcessGlobalPermissions(permissions);
253 permissions_ = permissions;
255 // Trusted Pepper plugins may be "internal", i.e. built-in to the browser
256 // binary. If we're being asked to load such a plugin (e.g. the Chromoting
257 // client) then fetch the entry points from the embedder, rather than a DLL.
258 std::vector<PepperPluginInfo> plugins;
259 GetContentClient()->AddPepperPlugins(&plugins);
260 for (size_t i = 0; i < plugins.size(); ++i) {
261 if (plugins[i].is_internal && plugins[i].path == path) {
262 // An internal plugin is being loaded, so fetch the entry points.
263 plugin_entry_points_ = plugins[i].internal_entry_points;
267 // If the plugin isn't internal then load it from |path|.
268 base::ScopedNativeLibrary library;
269 if (plugin_entry_points_.initialize_module == NULL) {
270 // Load the plugin from the specified library.
271 std::string error;
272 library.Reset(base::LoadNativeLibrary(path, &error));
273 if (!library.is_valid()) {
274 LOG(ERROR) << "Failed to load Pepper module from "
275 << path.value() << " (error: " << error << ")";
276 ReportLoadResult(path, LOAD_FAILED);
277 return;
280 // Get the GetInterface function (required).
281 plugin_entry_points_.get_interface =
282 reinterpret_cast<PP_GetInterface_Func>(
283 library.GetFunctionPointer("PPP_GetInterface"));
284 if (!plugin_entry_points_.get_interface) {
285 LOG(WARNING) << "No PPP_GetInterface in plugin library";
286 ReportLoadResult(path, ENTRY_POINT_MISSING);
287 return;
290 // The ShutdownModule/ShutdownBroker function is optional.
291 plugin_entry_points_.shutdown_module =
292 is_broker_ ?
293 reinterpret_cast<PP_ShutdownModule_Func>(
294 library.GetFunctionPointer("PPP_ShutdownBroker")) :
295 reinterpret_cast<PP_ShutdownModule_Func>(
296 library.GetFunctionPointer("PPP_ShutdownModule"));
298 if (!is_broker_) {
299 // Get the InitializeModule function (required for non-broker code).
300 plugin_entry_points_.initialize_module =
301 reinterpret_cast<PP_InitializeModule_Func>(
302 library.GetFunctionPointer("PPP_InitializeModule"));
303 if (!plugin_entry_points_.initialize_module) {
304 LOG(WARNING) << "No PPP_InitializeModule in plugin library";
305 ReportLoadResult(path, ENTRY_POINT_MISSING);
306 return;
311 #if defined(OS_WIN)
312 // If code subsequently tries to exit using abort(), force a crash (since
313 // otherwise these would be silent terminations and fly under the radar).
314 base::win::SetAbortBehaviorForCrashReporting();
315 if (permissions.HasPermission(ppapi::PERMISSION_FLASH)) {
316 // Force a crash for exit(), _exit(), or ExitProcess(), but only do that for
317 // Pepper Flash.
318 base::win::SetShouldCrashOnProcessDetach(true);
321 // Once we lower the token the sandbox is locked down and no new modules
322 // can be loaded. TODO(cpu): consider changing to the loading style of
323 // regular plugins.
324 if (g_target_services) {
325 // Let Flash load DRM before lockdown on Vista+.
326 if (permissions.HasPermission(ppapi::PERMISSION_FLASH) &&
327 base::win::OSInfo::GetInstance()->version() >=
328 base::win::VERSION_VISTA ) {
329 LoadLibrary(L"dxva2.dll");
332 // Cause advapi32 to load before the sandbox is turned on.
333 unsigned int dummy_rand;
334 rand_s(&dummy_rand);
335 // Warm up language subsystems before the sandbox is turned on.
336 ::GetUserDefaultLangID();
337 ::GetUserDefaultLCID();
339 g_target_services->LowerToken();
341 #endif
343 if (is_broker_) {
344 // Get the InitializeBroker function (required).
345 InitializeBrokerFunc init_broker =
346 reinterpret_cast<InitializeBrokerFunc>(
347 library.GetFunctionPointer("PPP_InitializeBroker"));
348 if (!init_broker) {
349 LOG(WARNING) << "No PPP_InitializeBroker in plugin library";
350 ReportLoadResult(path, ENTRY_POINT_MISSING);
351 return;
354 int32_t init_error = init_broker(&connect_instance_func_);
355 if (init_error != PP_OK) {
356 LOG(WARNING) << "InitBroker failed with error " << init_error;
357 ReportLoadResult(path, INIT_FAILED);
358 return;
360 if (!connect_instance_func_) {
361 LOG(WARNING) << "InitBroker did not provide PP_ConnectInstance_Func";
362 ReportLoadResult(path, INIT_FAILED);
363 return;
365 } else {
366 #if defined(OS_MACOSX)
367 // We need to do this after getting |PPP_GetInterface()| (or presumably
368 // doing something nontrivial with the library), else the sandbox
369 // intercedes.
370 CHECK(InitializeSandbox());
371 #endif
373 int32_t init_error = plugin_entry_points_.initialize_module(
374 local_pp_module_,
375 &ppapi::proxy::PluginDispatcher::GetBrowserInterface);
376 if (init_error != PP_OK) {
377 LOG(WARNING) << "InitModule failed with error " << init_error;
378 ReportLoadResult(path, INIT_FAILED);
379 return;
383 // Initialization succeeded, so keep the plugin DLL loaded.
384 library_.Reset(library.Release());
386 ReportLoadResult(path, LOAD_SUCCESS);
389 void PpapiThread::OnCreateChannel(base::ProcessId renderer_pid,
390 int renderer_child_id,
391 bool incognito) {
392 IPC::ChannelHandle channel_handle;
394 if (!plugin_entry_points_.get_interface || // Plugin couldn't be loaded.
395 !SetupRendererChannel(renderer_pid, renderer_child_id, incognito,
396 &channel_handle)) {
397 Send(new PpapiHostMsg_ChannelCreated(IPC::ChannelHandle()));
398 return;
401 Send(new PpapiHostMsg_ChannelCreated(channel_handle));
404 void PpapiThread::OnResourceReply(
405 const ppapi::proxy::ResourceMessageReplyParams& reply_params,
406 const IPC::Message& nested_msg) {
407 ppapi::proxy::PluginDispatcher::DispatchResourceReply(reply_params,
408 nested_msg);
411 void PpapiThread::OnSetNetworkState(bool online) {
412 // Note the browser-process side shouldn't send us these messages in the
413 // first unless the plugin has dev permissions, so we don't need to check
414 // again here. We don't want random plugins depending on this dev interface.
415 if (!plugin_entry_points_.get_interface)
416 return;
417 const PPP_NetworkState_Dev* ns = static_cast<const PPP_NetworkState_Dev*>(
418 plugin_entry_points_.get_interface(PPP_NETWORK_STATE_DEV_INTERFACE));
419 if (ns)
420 ns->SetOnLine(PP_FromBool(online));
423 void PpapiThread::OnCrash() {
424 // Intentionally crash upon the request of the browser.
425 volatile int* null_pointer = NULL;
426 *null_pointer = 0;
429 void PpapiThread::OnHang() {
430 // Intentionally hang upon the request of the browser.
431 for (;;)
432 base::PlatformThread::Sleep(base::TimeDelta::FromSeconds(1));
435 bool PpapiThread::SetupRendererChannel(base::ProcessId renderer_pid,
436 int renderer_child_id,
437 bool incognito,
438 IPC::ChannelHandle* handle) {
439 DCHECK(is_broker_ == (connect_instance_func_ != NULL));
440 IPC::ChannelHandle plugin_handle;
441 plugin_handle.name = IPC::Channel::GenerateVerifiedChannelID(
442 base::StringPrintf(
443 "%d.r%d", base::GetCurrentProcId(), renderer_child_id));
445 ppapi::proxy::ProxyChannel* dispatcher = NULL;
446 bool init_result = false;
447 if (is_broker_) {
448 BrokerProcessDispatcher* broker_dispatcher =
449 new BrokerProcessDispatcher(plugin_entry_points_.get_interface,
450 connect_instance_func_);
451 init_result = broker_dispatcher->InitBrokerWithChannel(this,
452 renderer_pid,
453 plugin_handle,
454 false);
455 dispatcher = broker_dispatcher;
456 } else {
457 PluginProcessDispatcher* plugin_dispatcher =
458 new PluginProcessDispatcher(plugin_entry_points_.get_interface,
459 permissions_,
460 incognito);
461 init_result = plugin_dispatcher->InitPluginWithChannel(this,
462 renderer_pid,
463 plugin_handle,
464 false);
465 dispatcher = plugin_dispatcher;
468 if (!init_result) {
469 delete dispatcher;
470 return false;
473 handle->name = plugin_handle.name;
474 #if defined(OS_POSIX)
475 // On POSIX, transfer ownership of the renderer-side (client) FD.
476 // This ensures this process will be notified when it is closed even if a
477 // connection is not established.
478 handle->socket = base::FileDescriptor(dispatcher->TakeRendererFD(), true);
479 if (handle->socket.fd == -1)
480 return false;
481 #endif
483 // From here, the dispatcher will manage its own lifetime according to the
484 // lifetime of the attached channel.
485 return true;
488 void PpapiThread::SavePluginName(const base::FilePath& path) {
489 ppapi::proxy::PluginGlobals::Get()->set_plugin_name(
490 path.BaseName().AsUTF8Unsafe());
492 // plugin() is NULL when in-process, which is fine, because this is
493 // just a hook for setting the process name.
494 if (GetContentClient()->plugin()) {
495 GetContentClient()->plugin()->PluginProcessStarted(
496 path.BaseName().RemoveExtension().LossyDisplayName());
500 void PpapiThread::ReportLoadResult(const base::FilePath& path,
501 LoadResult result) {
502 DCHECK_LT(result, LOAD_RESULT_MAX);
504 std::ostringstream histogram_name;
505 histogram_name << "Plugin.Ppapi" << (is_broker_ ? "Broker" : "Plugin")
506 << "LoadResult_" << path.BaseName().MaybeAsASCII();
508 // Note: This leaks memory, which is expected behavior.
509 base::HistogramBase* histogram =
510 base::LinearHistogram::FactoryGet(
511 histogram_name.str(),
513 LOAD_RESULT_MAX,
514 LOAD_RESULT_MAX + 1,
515 base::HistogramBase::kUmaTargetedHistogramFlag);
517 histogram->Add(result);
520 } // namespace content