- add sources.
[platform/framework/web/crosswalk.git] / src / content / ppapi_plugin / ppapi_thread.cc
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.
4
5 #include "content/ppapi_plugin/ppapi_thread.h"
6
7 #include <limits>
8
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"
43
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
51
52 #if defined(OS_WIN)
53 extern sandbox::TargetServices* g_target_services;
54 #else
55 extern void* g_target_services;
56 #endif
57
58 namespace content {
59
60 typedef int32_t (*InitializeBrokerFunc)
61     (PP_ConnectInstance_Func* connect_instance_func);
62
63 PpapiThread::DispatcherMessageListener::DispatcherMessageListener(
64     PpapiThread* owner) : owner_(owner) {
65 }
66
67 PpapiThread::DispatcherMessageListener::~DispatcherMessageListener() {
68 }
69
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;
78   }
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);
83
84   return false;
85 }
86
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));
98
99   webkit_platform_support_.reset(new PpapiWebKitPlatformSupportImpl);
100   WebKit::initialize(webkit_platform_support_.get());
101
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_HOSTRESOLVER_PRIVATE,
105            &dispatcher_message_listener_);
106 }
107
108 PpapiThread::~PpapiThread() {
109 }
110
111 void PpapiThread::Shutdown() {
112   ppapi::proxy::PluginGlobals::Get()->set_plugin_proxy_delegate(NULL);
113   if (plugin_entry_points_.shutdown_module)
114     plugin_entry_points_.shutdown_module();
115   WebKit::shutdown();
116 }
117
118 bool PpapiThread::Send(IPC::Message* msg) {
119   // Allow access from multiple threads.
120   if (base::MessageLoop::current() == message_loop())
121     return ChildThread::Send(msg);
122
123   return sync_message_filter()->Send(msg);
124 }
125
126 // Note that this function is called only for messages from the channel to the
127 // browser process. Messages from the renderer process are sent via a different
128 // channel that ends up at Dispatcher::OnMessageReceived.
129 bool PpapiThread::OnControlMessageReceived(const IPC::Message& msg) {
130   bool handled = true;
131   IPC_BEGIN_MESSAGE_MAP(PpapiThread, msg)
132     IPC_MESSAGE_HANDLER(PpapiMsg_LoadPlugin, OnLoadPlugin)
133     IPC_MESSAGE_HANDLER(PpapiMsg_CreateChannel, OnCreateChannel)
134     IPC_MESSAGE_HANDLER(PpapiMsg_SetNetworkState, OnSetNetworkState)
135     IPC_MESSAGE_HANDLER(PpapiMsg_Crash, OnCrash)
136     IPC_MESSAGE_HANDLER(PpapiMsg_Hang, OnHang)
137     IPC_MESSAGE_HANDLER(PpapiPluginMsg_ResourceReply, OnResourceReply)
138     IPC_MESSAGE_UNHANDLED(handled = false)
139   IPC_END_MESSAGE_MAP()
140   return handled;
141 }
142
143 void PpapiThread::OnChannelConnected(int32 peer_pid) {
144   ChildThread::OnChannelConnected(peer_pid);
145 #if defined(OS_WIN)
146   if (is_broker_)
147     peer_handle_.Set(::OpenProcess(PROCESS_DUP_HANDLE, FALSE, peer_pid));
148 #endif
149 }
150
151 base::MessageLoopProxy* PpapiThread::GetIPCMessageLoop() {
152   return ChildProcess::current()->io_message_loop_proxy();
153 }
154
155 base::WaitableEvent* PpapiThread::GetShutdownEvent() {
156   return ChildProcess::current()->GetShutDownEvent();
157 }
158
159 IPC::PlatformFileForTransit PpapiThread::ShareHandleWithRemote(
160     base::PlatformFile handle,
161     base::ProcessId peer_pid,
162     bool should_close_source) {
163 #if defined(OS_WIN)
164   if (peer_handle_.IsValid()) {
165     DCHECK(is_broker_);
166     return IPC::GetFileHandleForProcess(handle, peer_handle_,
167                                         should_close_source);
168   }
169 #endif
170
171   DCHECK(peer_pid != base::kNullProcessId);
172   return BrokerGetFileHandleForProcess(handle, peer_pid, should_close_source);
173 }
174
175 std::set<PP_Instance>* PpapiThread::GetGloballySeenInstanceIDSet() {
176   return &globally_seen_instance_ids_;
177 }
178
179 IPC::Sender* PpapiThread::GetBrowserSender() {
180   return this;
181 }
182
183 std::string PpapiThread::GetUILanguage() {
184   CommandLine* command_line = CommandLine::ForCurrentProcess();
185   return command_line->GetSwitchValueASCII(switches::kLang);
186 }
187
188 void PpapiThread::PreCacheFont(const void* logfontw) {
189 #if defined(OS_WIN)
190   Send(new ChildProcessHostMsg_PreCacheFont(
191       *static_cast<const LOGFONTW*>(logfontw)));
192 #endif
193 }
194
195 void PpapiThread::SetActiveURL(const std::string& url) {
196   GetContentClient()->SetActiveURL(GURL(url));
197 }
198
199 PP_Resource PpapiThread::CreateBrowserFont(
200     ppapi::proxy::Connection connection,
201     PP_Instance instance,
202     const PP_BrowserFont_Trusted_Description& desc,
203     const ppapi::Preferences& prefs) {
204   if (!BrowserFontResource_Trusted::IsPPFontDescriptionValid(desc))
205     return 0;
206   return (new BrowserFontResource_Trusted(
207         connection, instance, desc, prefs))->GetReference();
208 }
209
210 uint32 PpapiThread::Register(ppapi::proxy::PluginDispatcher* plugin_dispatcher) {
211   if (!plugin_dispatcher ||
212       plugin_dispatchers_.size() >= std::numeric_limits<uint32>::max()) {
213     return 0;
214   }
215
216   uint32 id = 0;
217   do {
218     // Although it is unlikely, make sure that we won't cause any trouble when
219     // the counter overflows.
220     id = next_plugin_dispatcher_id_++;
221   } while (id == 0 ||
222            plugin_dispatchers_.find(id) != plugin_dispatchers_.end());
223   plugin_dispatchers_[id] = plugin_dispatcher;
224   return id;
225 }
226
227 void PpapiThread::Unregister(uint32 plugin_dispatcher_id) {
228   plugin_dispatchers_.erase(plugin_dispatcher_id);
229 }
230
231 void PpapiThread::OnLoadPlugin(const base::FilePath& path,
232                                const ppapi::PpapiPermissions& permissions) {
233   // In case of crashes, the crash dump doesn't indicate which plugin
234   // it came from.
235   base::debug::SetCrashKeyValue("ppapi_path", path.MaybeAsASCII());
236
237   SavePluginName(path);
238
239   // This must be set before calling into the plugin so it can get the
240   // interfaces it has permission for.
241   ppapi::proxy::InterfaceList::SetProcessGlobalPermissions(permissions);
242   permissions_ = permissions;
243
244   // Trusted Pepper plugins may be "internal", i.e. built-in to the browser
245   // binary.  If we're being asked to load such a plugin (e.g. the Chromoting
246   // client) then fetch the entry points from the embedder, rather than a DLL.
247   std::vector<PepperPluginInfo> plugins;
248   GetContentClient()->AddPepperPlugins(&plugins);
249   for (size_t i = 0; i < plugins.size(); ++i) {
250     if (plugins[i].is_internal && plugins[i].path == path) {
251       // An internal plugin is being loaded, so fetch the entry points.
252       plugin_entry_points_ = plugins[i].internal_entry_points;
253     }
254   }
255
256   // If the plugin isn't internal then load it from |path|.
257   base::ScopedNativeLibrary library;
258   if (plugin_entry_points_.initialize_module == NULL) {
259     // Load the plugin from the specified library.
260     std::string error;
261     library.Reset(base::LoadNativeLibrary(path, &error));
262     if (!library.is_valid()) {
263       LOG(ERROR) << "Failed to load Pepper module from "
264         << path.value() << " (error: " << error << ")";
265       ReportLoadResult(path, LOAD_FAILED);
266       return;
267     }
268
269     // Get the GetInterface function (required).
270     plugin_entry_points_.get_interface =
271         reinterpret_cast<PP_GetInterface_Func>(
272             library.GetFunctionPointer("PPP_GetInterface"));
273     if (!plugin_entry_points_.get_interface) {
274       LOG(WARNING) << "No PPP_GetInterface in plugin library";
275       ReportLoadResult(path, ENTRY_POINT_MISSING);
276       return;
277     }
278
279     // The ShutdownModule/ShutdownBroker function is optional.
280     plugin_entry_points_.shutdown_module =
281         is_broker_ ?
282         reinterpret_cast<PP_ShutdownModule_Func>(
283             library.GetFunctionPointer("PPP_ShutdownBroker")) :
284         reinterpret_cast<PP_ShutdownModule_Func>(
285             library.GetFunctionPointer("PPP_ShutdownModule"));
286
287     if (!is_broker_) {
288       // Get the InitializeModule function (required for non-broker code).
289       plugin_entry_points_.initialize_module =
290           reinterpret_cast<PP_InitializeModule_Func>(
291               library.GetFunctionPointer("PPP_InitializeModule"));
292       if (!plugin_entry_points_.initialize_module) {
293         LOG(WARNING) << "No PPP_InitializeModule in plugin library";
294         ReportLoadResult(path, ENTRY_POINT_MISSING);
295         return;
296       }
297     }
298   }
299
300 #if defined(OS_WIN)
301   // If code subsequently tries to exit using abort(), force a crash (since
302   // otherwise these would be silent terminations and fly under the radar).
303   base::win::SetAbortBehaviorForCrashReporting();
304
305   // Once we lower the token the sandbox is locked down and no new modules
306   // can be loaded. TODO(cpu): consider changing to the loading style of
307   // regular plugins.
308   if (g_target_services) {
309     // Let Flash load DRM before lockdown on Vista+.
310     if (permissions.HasPermission(ppapi::PERMISSION_FLASH) &&
311         base::win::OSInfo::GetInstance()->version() >=
312         base::win::VERSION_VISTA ) {
313       LoadLibrary(L"dxva2.dll");
314     }
315
316     // Cause advapi32 to load before the sandbox is turned on.
317     unsigned int dummy_rand;
318     rand_s(&dummy_rand);
319     // Warm up language subsystems before the sandbox is turned on.
320     ::GetUserDefaultLangID();
321     ::GetUserDefaultLCID();
322
323     g_target_services->LowerToken();
324   }
325 #endif
326
327   if (is_broker_) {
328     // Get the InitializeBroker function (required).
329     InitializeBrokerFunc init_broker =
330         reinterpret_cast<InitializeBrokerFunc>(
331             library.GetFunctionPointer("PPP_InitializeBroker"));
332     if (!init_broker) {
333       LOG(WARNING) << "No PPP_InitializeBroker in plugin library";
334       ReportLoadResult(path, ENTRY_POINT_MISSING);
335       return;
336     }
337
338     int32_t init_error = init_broker(&connect_instance_func_);
339     if (init_error != PP_OK) {
340       LOG(WARNING) << "InitBroker failed with error " << init_error;
341       ReportLoadResult(path, INIT_FAILED);
342       return;
343     }
344     if (!connect_instance_func_) {
345       LOG(WARNING) << "InitBroker did not provide PP_ConnectInstance_Func";
346       ReportLoadResult(path, INIT_FAILED);
347       return;
348     }
349   } else {
350 #if defined(OS_MACOSX)
351     // We need to do this after getting |PPP_GetInterface()| (or presumably
352     // doing something nontrivial with the library), else the sandbox
353     // intercedes.
354     CHECK(InitializeSandbox());
355 #endif
356
357     int32_t init_error = plugin_entry_points_.initialize_module(
358         local_pp_module_,
359         &ppapi::proxy::PluginDispatcher::GetBrowserInterface);
360     if (init_error != PP_OK) {
361       LOG(WARNING) << "InitModule failed with error " << init_error;
362       ReportLoadResult(path, INIT_FAILED);
363       return;
364     }
365   }
366
367   // Initialization succeeded, so keep the plugin DLL loaded.
368   library_.Reset(library.Release());
369
370   ReportLoadResult(path, LOAD_SUCCESS);
371 }
372
373 void PpapiThread::OnCreateChannel(base::ProcessId renderer_pid,
374                                   int renderer_child_id,
375                                   bool incognito) {
376   IPC::ChannelHandle channel_handle;
377
378   if (!plugin_entry_points_.get_interface ||  // Plugin couldn't be loaded.
379       !SetupRendererChannel(renderer_pid, renderer_child_id, incognito,
380                             &channel_handle)) {
381     Send(new PpapiHostMsg_ChannelCreated(IPC::ChannelHandle()));
382     return;
383   }
384
385   Send(new PpapiHostMsg_ChannelCreated(channel_handle));
386 }
387
388 void PpapiThread::OnResourceReply(
389     const ppapi::proxy::ResourceMessageReplyParams& reply_params,
390     const IPC::Message& nested_msg) {
391   ppapi::proxy::PluginDispatcher::DispatchResourceReply(reply_params,
392                                                         nested_msg);
393 }
394
395 void PpapiThread::OnSetNetworkState(bool online) {
396   // Note the browser-process side shouldn't send us these messages in the
397   // first unless the plugin has dev permissions, so we don't need to check
398   // again here. We don't want random plugins depending on this dev interface.
399   if (!plugin_entry_points_.get_interface)
400     return;
401   const PPP_NetworkState_Dev* ns = static_cast<const PPP_NetworkState_Dev*>(
402       plugin_entry_points_.get_interface(PPP_NETWORK_STATE_DEV_INTERFACE));
403   if (ns)
404     ns->SetOnLine(PP_FromBool(online));
405 }
406
407 void PpapiThread::OnCrash() {
408   // Intentionally crash upon the request of the browser.
409   volatile int* null_pointer = NULL;
410   *null_pointer = 0;
411 }
412
413 void PpapiThread::OnHang() {
414   // Intentionally hang upon the request of the browser.
415   for (;;)
416     base::PlatformThread::Sleep(base::TimeDelta::FromSeconds(1));
417 }
418
419 bool PpapiThread::SetupRendererChannel(base::ProcessId renderer_pid,
420                                        int renderer_child_id,
421                                        bool incognito,
422                                        IPC::ChannelHandle* handle) {
423   DCHECK(is_broker_ == (connect_instance_func_ != NULL));
424   IPC::ChannelHandle plugin_handle;
425   plugin_handle.name = IPC::Channel::GenerateVerifiedChannelID(
426       base::StringPrintf(
427           "%d.r%d", base::GetCurrentProcId(), renderer_child_id));
428
429   ppapi::proxy::ProxyChannel* dispatcher = NULL;
430   bool init_result = false;
431   if (is_broker_) {
432     BrokerProcessDispatcher* broker_dispatcher =
433         new BrokerProcessDispatcher(plugin_entry_points_.get_interface,
434                                     connect_instance_func_);
435     init_result = broker_dispatcher->InitBrokerWithChannel(this,
436                                                            renderer_pid,
437                                                            plugin_handle,
438                                                            false);
439     dispatcher = broker_dispatcher;
440   } else {
441     PluginProcessDispatcher* plugin_dispatcher =
442         new PluginProcessDispatcher(plugin_entry_points_.get_interface,
443                                     permissions_,
444                                     incognito);
445     init_result = plugin_dispatcher->InitPluginWithChannel(this,
446                                                            renderer_pid,
447                                                            plugin_handle,
448                                                            false);
449     dispatcher = plugin_dispatcher;
450   }
451
452   if (!init_result) {
453     delete dispatcher;
454     return false;
455   }
456
457   handle->name = plugin_handle.name;
458 #if defined(OS_POSIX)
459   // On POSIX, transfer ownership of the renderer-side (client) FD.
460   // This ensures this process will be notified when it is closed even if a
461   // connection is not established.
462   handle->socket = base::FileDescriptor(dispatcher->TakeRendererFD(), true);
463   if (handle->socket.fd == -1)
464     return false;
465 #endif
466
467   // From here, the dispatcher will manage its own lifetime according to the
468   // lifetime of the attached channel.
469   return true;
470 }
471
472 void PpapiThread::SavePluginName(const base::FilePath& path) {
473   ppapi::proxy::PluginGlobals::Get()->set_plugin_name(
474       path.BaseName().AsUTF8Unsafe());
475
476   // plugin() is NULL when in-process, which is fine, because this is
477   // just a hook for setting the process name.
478   if (GetContentClient()->plugin()) {
479     GetContentClient()->plugin()->PluginProcessStarted(
480         path.BaseName().RemoveExtension().LossyDisplayName());
481   }
482 }
483
484 void PpapiThread::ReportLoadResult(const base::FilePath& path,
485                                    LoadResult result) {
486   DCHECK_LT(result, LOAD_RESULT_MAX);
487
488   std::ostringstream histogram_name;
489   histogram_name << "Plugin.Ppapi" << (is_broker_ ? "Broker" : "Plugin")
490                  << "LoadResult_" << path.BaseName().MaybeAsASCII();
491
492   // Note: This leaks memory, which is expected behavior.
493   base::HistogramBase* histogram =
494       base::LinearHistogram::FactoryGet(
495           histogram_name.str(),
496           1,
497           LOAD_RESULT_MAX,
498           LOAD_RESULT_MAX + 1,
499           base::HistogramBase::kUmaTargetedHistogramFlag);
500
501   histogram->Add(result);
502 }
503
504 }  // namespace content