Upstream version 10.39.225.0
[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/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_webkitplatformsupport_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"
47
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
55
56 #if defined(OS_WIN)
57 const char kWidevineCdmAdapterFileName[] = "widevinecdmadapter.dll";
58
59 extern sandbox::TargetServices* g_target_services;
60
61 // Used by EnumSystemLocales for warming up.
62 static BOOL CALLBACK EnumLocalesProc(LPTSTR lpLocaleString) {
63   return TRUE;
64 }
65
66 static BOOL CALLBACK EnumLocalesProcEx(
67     LPWSTR lpLocaleString,
68     DWORD dwFlags,
69     LPARAM lParam) {
70   return TRUE;
71 }
72
73 // Warm up language subsystems before the sandbox is turned on.
74 static void WarmupWindowsLocales(const ppapi::PpapiPermissions& permissions) {
75   ::GetUserDefaultLangID();
76   ::GetUserDefaultLCID();
77
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);
82
83       HMODULE handle_kern32 = GetModuleHandleW(L"Kernel32.dll");
84       PfnEnumSystemLocalesEx enum_sys_locales_ex =
85           reinterpret_cast<PfnEnumSystemLocalesEx>
86               (GetProcAddress(handle_kern32, "EnumSystemLocalesEx"));
87
88       enum_sys_locales_ex(EnumLocalesProcEx, LOCALE_WINDOWS, 0, 0);
89     } else {
90       EnumSystemLocalesW(EnumLocalesProc, LCID_INSTALLED);
91     }
92   }
93 }
94 #else
95 extern void* g_target_services;
96 #endif
97
98 namespace content {
99
100 typedef int32_t (*InitializeBrokerFunc)
101     (PP_ConnectInstance_Func* connect_instance_func);
102
103 PpapiThread::PpapiThread(const CommandLine& command_line, bool is_broker)
104     : is_broker_(is_broker),
105       connect_instance_func_(NULL),
106       local_pp_module_(
107           base::RandInt(0, std::numeric_limits<PP_Module>::max())),
108       next_plugin_dispatcher_id_(1) {
109   ppapi::proxy::PluginGlobals* globals = ppapi::proxy::PluginGlobals::Get();
110   globals->SetPluginProxyDelegate(this);
111   globals->set_command_line(
112       command_line.GetSwitchValueASCII(switches::kPpapiFlashArgs));
113
114   webkit_platform_support_.reset(new PpapiWebKitPlatformSupportImpl);
115   blink::initialize(webkit_platform_support_.get());
116
117   if (!is_broker_) {
118     channel()->AddFilter(
119         new ppapi::proxy::PluginMessageFilter(
120             NULL, globals->resource_reply_thread_registrar()));
121   }
122 }
123
124 PpapiThread::~PpapiThread() {
125 }
126
127 void PpapiThread::Shutdown() {
128   ChildThread::Shutdown();
129
130   ppapi::proxy::PluginGlobals::Get()->ResetPluginProxyDelegate();
131   if (plugin_entry_points_.shutdown_module)
132     plugin_entry_points_.shutdown_module();
133   webkit_platform_support_->Shutdown();
134   blink::shutdown();
135 }
136
137 bool PpapiThread::Send(IPC::Message* msg) {
138   // Allow access from multiple threads.
139   if (base::MessageLoop::current() == message_loop())
140     return ChildThread::Send(msg);
141
142   return sync_message_filter()->Send(msg);
143 }
144
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;
159 }
160
161 void PpapiThread::OnChannelConnected(int32 peer_pid) {
162   ChildThread::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
167 }
168
169 base::MessageLoopProxy* PpapiThread::GetIPCMessageLoop() {
170   return ChildProcess::current()->io_message_loop_proxy();
171 }
172
173 base::WaitableEvent* PpapiThread::GetShutdownEvent() {
174   return ChildProcess::current()->GetShutDownEvent();
175 }
176
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);
186   }
187 #endif
188
189   DCHECK(peer_pid != base::kNullProcessId);
190   return BrokerGetFileHandleForProcess(handle, peer_pid, should_close_source);
191 }
192
193 std::set<PP_Instance>* PpapiThread::GetGloballySeenInstanceIDSet() {
194   return &globally_seen_instance_ids_;
195 }
196
197 IPC::Sender* PpapiThread::GetBrowserSender() {
198   return this;
199 }
200
201 std::string PpapiThread::GetUILanguage() {
202   CommandLine* command_line = CommandLine::ForCurrentProcess();
203   return command_line->GetSwitchValueASCII(switches::kLang);
204 }
205
206 void PpapiThread::PreCacheFont(const void* logfontw) {
207 #if defined(OS_WIN)
208   Send(new ChildProcessHostMsg_PreCacheFont(
209       *static_cast<const LOGFONTW*>(logfontw)));
210 #endif
211 }
212
213 void PpapiThread::SetActiveURL(const std::string& url) {
214   GetContentClient()->SetActiveURL(GURL(url));
215 }
216
217 PP_Resource PpapiThread::CreateBrowserFont(
218     ppapi::proxy::Connection connection,
219     PP_Instance instance,
220     const PP_BrowserFont_Trusted_Description& desc,
221     const ppapi::Preferences& prefs) {
222   if (!BrowserFontResource_Trusted::IsPPFontDescriptionValid(desc))
223     return 0;
224   return (new BrowserFontResource_Trusted(
225         connection, instance, desc, prefs))->GetReference();
226 }
227
228 uint32 PpapiThread::Register(ppapi::proxy::PluginDispatcher* plugin_dispatcher) {
229   if (!plugin_dispatcher ||
230       plugin_dispatchers_.size() >= std::numeric_limits<uint32>::max()) {
231     return 0;
232   }
233
234   uint32 id = 0;
235   do {
236     // Although it is unlikely, make sure that we won't cause any trouble when
237     // the counter overflows.
238     id = next_plugin_dispatcher_id_++;
239   } while (id == 0 ||
240            plugin_dispatchers_.find(id) != plugin_dispatchers_.end());
241   plugin_dispatchers_[id] = plugin_dispatcher;
242   return id;
243 }
244
245 void PpapiThread::Unregister(uint32 plugin_dispatcher_id) {
246   plugin_dispatchers_.erase(plugin_dispatcher_id);
247 }
248
249 void PpapiThread::OnLoadPlugin(const base::FilePath& path,
250                                const ppapi::PpapiPermissions& permissions) {
251   // In case of crashes, the crash dump doesn't indicate which plugin
252   // it came from.
253   base::debug::SetCrashKeyValue("ppapi_path", path.MaybeAsASCII());
254
255   SavePluginName(path);
256
257   // This must be set before calling into the plugin so it can get the
258   // interfaces it has permission for.
259   ppapi::proxy::InterfaceList::SetProcessGlobalPermissions(permissions);
260   permissions_ = permissions;
261
262   // Trusted Pepper plugins may be "internal", i.e. built-in to the browser
263   // binary.  If we're being asked to load such a plugin (e.g. the Chromoting
264   // client) then fetch the entry points from the embedder, rather than a DLL.
265   std::vector<PepperPluginInfo> plugins;
266   GetContentClient()->AddPepperPlugins(&plugins);
267   for (size_t i = 0; i < plugins.size(); ++i) {
268     if (plugins[i].is_internal && plugins[i].path == path) {
269       // An internal plugin is being loaded, so fetch the entry points.
270       plugin_entry_points_ = plugins[i].internal_entry_points;
271     }
272   }
273
274   // If the plugin isn't internal then load it from |path|.
275   base::ScopedNativeLibrary library;
276   if (plugin_entry_points_.initialize_module == NULL) {
277     // Load the plugin from the specified library.
278     base::NativeLibraryLoadError error;
279     library.Reset(base::LoadNativeLibrary(path, &error));
280     if (!library.is_valid()) {
281       LOG(ERROR) << "Failed to load Pepper module from " << path.value()
282                  << " (error: " << error.ToString() << ")";
283       if (!base::PathExists(path)) {
284         ReportLoadResult(path, FILE_MISSING);
285         return;
286       }
287       ReportLoadResult(path, LOAD_FAILED);
288       // Report detailed reason for load failure.
289       ReportLoadErrorCode(path, error);
290       return;
291     }
292
293     // Get the GetInterface function (required).
294     plugin_entry_points_.get_interface =
295         reinterpret_cast<PP_GetInterface_Func>(
296             library.GetFunctionPointer("PPP_GetInterface"));
297     if (!plugin_entry_points_.get_interface) {
298       LOG(WARNING) << "No PPP_GetInterface in plugin library";
299       ReportLoadResult(path, ENTRY_POINT_MISSING);
300       return;
301     }
302
303     // The ShutdownModule/ShutdownBroker function is optional.
304     plugin_entry_points_.shutdown_module =
305         is_broker_ ?
306         reinterpret_cast<PP_ShutdownModule_Func>(
307             library.GetFunctionPointer("PPP_ShutdownBroker")) :
308         reinterpret_cast<PP_ShutdownModule_Func>(
309             library.GetFunctionPointer("PPP_ShutdownModule"));
310
311     if (!is_broker_) {
312       // Get the InitializeModule function (required for non-broker code).
313       plugin_entry_points_.initialize_module =
314           reinterpret_cast<PP_InitializeModule_Func>(
315               library.GetFunctionPointer("PPP_InitializeModule"));
316       if (!plugin_entry_points_.initialize_module) {
317         LOG(WARNING) << "No PPP_InitializeModule in plugin library";
318         ReportLoadResult(path, ENTRY_POINT_MISSING);
319         return;
320       }
321     }
322   }
323
324 #if defined(OS_WIN)
325   // If code subsequently tries to exit using abort(), force a crash (since
326   // otherwise these would be silent terminations and fly under the radar).
327   base::win::SetAbortBehaviorForCrashReporting();
328
329   // Once we lower the token the sandbox is locked down and no new modules
330   // can be loaded. TODO(cpu): consider changing to the loading style of
331   // regular plugins.
332   if (g_target_services) {
333     // Let Flash and Widevine CDM adapter load DXVA before lockdown on Vista+.
334     if (permissions.HasPermission(ppapi::PERMISSION_FLASH) ||
335         path.BaseName().MaybeAsASCII() == kWidevineCdmAdapterFileName) {
336       if (base::win::OSInfo::GetInstance()->version() >=
337           base::win::VERSION_VISTA) {
338         LoadLibraryA("dxva2.dll");
339       }
340     }
341
342     if (permissions.HasPermission(ppapi::PERMISSION_FLASH)) {
343       if (base::win::OSInfo::GetInstance()->version() >=
344           base::win::VERSION_WIN7) {
345         base::CPU cpu;
346         if (cpu.vendor_name() == "AuthenticAMD") {
347           // The AMD crypto acceleration is only AMD Bulldozer and above.
348 #if defined(_WIN64)
349           LoadLibraryA("amdhcp64.dll");
350 #else
351           LoadLibraryA("amdhcp32.dll");
352 #endif
353         }
354       }
355     }
356
357     // Cause advapi32 to load before the sandbox is turned on.
358     unsigned int dummy_rand;
359     rand_s(&dummy_rand);
360
361     WarmupWindowsLocales(permissions);
362
363     g_target_services->LowerToken();
364   }
365 #endif
366
367   if (is_broker_) {
368     // Get the InitializeBroker function (required).
369     InitializeBrokerFunc init_broker =
370         reinterpret_cast<InitializeBrokerFunc>(
371             library.GetFunctionPointer("PPP_InitializeBroker"));
372     if (!init_broker) {
373       LOG(WARNING) << "No PPP_InitializeBroker in plugin library";
374       ReportLoadResult(path, ENTRY_POINT_MISSING);
375       return;
376     }
377
378     int32_t init_error = init_broker(&connect_instance_func_);
379     if (init_error != PP_OK) {
380       LOG(WARNING) << "InitBroker failed with error " << init_error;
381       ReportLoadResult(path, INIT_FAILED);
382       return;
383     }
384     if (!connect_instance_func_) {
385       LOG(WARNING) << "InitBroker did not provide PP_ConnectInstance_Func";
386       ReportLoadResult(path, INIT_FAILED);
387       return;
388     }
389   } else {
390 #if defined(OS_MACOSX)
391     // We need to do this after getting |PPP_GetInterface()| (or presumably
392     // doing something nontrivial with the library), else the sandbox
393     // intercedes.
394     CHECK(InitializeSandbox());
395 #endif
396
397     int32_t init_error = plugin_entry_points_.initialize_module(
398         local_pp_module_,
399         &ppapi::proxy::PluginDispatcher::GetBrowserInterface);
400     if (init_error != PP_OK) {
401       LOG(WARNING) << "InitModule failed with error " << init_error;
402       ReportLoadResult(path, INIT_FAILED);
403       return;
404     }
405   }
406
407   // Initialization succeeded, so keep the plugin DLL loaded.
408   library_.Reset(library.Release());
409
410   ReportLoadResult(path, LOAD_SUCCESS);
411 }
412
413 void PpapiThread::OnCreateChannel(base::ProcessId renderer_pid,
414                                   int renderer_child_id,
415                                   bool incognito) {
416   IPC::ChannelHandle channel_handle;
417
418   if (!plugin_entry_points_.get_interface ||  // Plugin couldn't be loaded.
419       !SetupRendererChannel(renderer_pid, renderer_child_id, incognito,
420                             &channel_handle)) {
421     Send(new PpapiHostMsg_ChannelCreated(IPC::ChannelHandle()));
422     return;
423   }
424
425   Send(new PpapiHostMsg_ChannelCreated(channel_handle));
426 }
427
428 void PpapiThread::OnSetNetworkState(bool online) {
429   // Note the browser-process side shouldn't send us these messages in the
430   // first unless the plugin has dev permissions, so we don't need to check
431   // again here. We don't want random plugins depending on this dev interface.
432   if (!plugin_entry_points_.get_interface)
433     return;
434   const PPP_NetworkState_Dev* ns = static_cast<const PPP_NetworkState_Dev*>(
435       plugin_entry_points_.get_interface(PPP_NETWORK_STATE_DEV_INTERFACE));
436   if (ns)
437     ns->SetOnLine(PP_FromBool(online));
438 }
439
440 void PpapiThread::OnCrash() {
441   // Intentionally crash upon the request of the browser.
442   volatile int* null_pointer = NULL;
443   *null_pointer = 0;
444 }
445
446 void PpapiThread::OnHang() {
447   // Intentionally hang upon the request of the browser.
448   for (;;)
449     base::PlatformThread::Sleep(base::TimeDelta::FromSeconds(1));
450 }
451
452 bool PpapiThread::SetupRendererChannel(base::ProcessId renderer_pid,
453                                        int renderer_child_id,
454                                        bool incognito,
455                                        IPC::ChannelHandle* handle) {
456   DCHECK(is_broker_ == (connect_instance_func_ != NULL));
457   IPC::ChannelHandle plugin_handle;
458   plugin_handle.name = IPC::Channel::GenerateVerifiedChannelID(
459       base::StringPrintf(
460           "%d.r%d", base::GetCurrentProcId(), renderer_child_id));
461
462   ppapi::proxy::ProxyChannel* dispatcher = NULL;
463   bool init_result = false;
464   if (is_broker_) {
465     BrokerProcessDispatcher* broker_dispatcher =
466         new BrokerProcessDispatcher(plugin_entry_points_.get_interface,
467                                     connect_instance_func_);
468     init_result = broker_dispatcher->InitBrokerWithChannel(this,
469                                                            renderer_pid,
470                                                            plugin_handle,
471                                                            false);
472     dispatcher = broker_dispatcher;
473   } else {
474     PluginProcessDispatcher* plugin_dispatcher =
475         new PluginProcessDispatcher(plugin_entry_points_.get_interface,
476                                     permissions_,
477                                     incognito);
478     init_result = plugin_dispatcher->InitPluginWithChannel(this,
479                                                            renderer_pid,
480                                                            plugin_handle,
481                                                            false);
482     dispatcher = plugin_dispatcher;
483   }
484
485   if (!init_result) {
486     delete dispatcher;
487     return false;
488   }
489
490   handle->name = plugin_handle.name;
491 #if defined(OS_POSIX)
492   // On POSIX, transfer ownership of the renderer-side (client) FD.
493   // This ensures this process will be notified when it is closed even if a
494   // connection is not established.
495   handle->socket = base::FileDescriptor(dispatcher->TakeRendererFD(), true);
496   if (handle->socket.fd == -1)
497     return false;
498 #endif
499
500   // From here, the dispatcher will manage its own lifetime according to the
501   // lifetime of the attached channel.
502   return true;
503 }
504
505 void PpapiThread::SavePluginName(const base::FilePath& path) {
506   ppapi::proxy::PluginGlobals::Get()->set_plugin_name(
507       path.BaseName().AsUTF8Unsafe());
508
509   // plugin() is NULL when in-process, which is fine, because this is
510   // just a hook for setting the process name.
511   if (GetContentClient()->plugin()) {
512     GetContentClient()->plugin()->PluginProcessStarted(
513         path.BaseName().RemoveExtension().LossyDisplayName());
514   }
515 }
516
517 void PpapiThread::ReportLoadResult(const base::FilePath& path,
518                                    LoadResult result) {
519   DCHECK_LT(result, LOAD_RESULT_MAX);
520   std::string histogram_name = std::string("Plugin.Ppapi") +
521                                (is_broker_ ? "Broker" : "Plugin") +
522                                "LoadResult_" + path.BaseName().MaybeAsASCII();
523
524   // Note: This leaks memory, which is expected behavior.
525   base::HistogramBase* histogram =
526       base::LinearHistogram::FactoryGet(
527           histogram_name,
528           1,
529           LOAD_RESULT_MAX,
530           LOAD_RESULT_MAX + 1,
531           base::HistogramBase::kUmaTargetedHistogramFlag);
532
533   histogram->Add(result);
534 }
535
536 void PpapiThread::ReportLoadErrorCode(
537     const base::FilePath& path,
538     const base::NativeLibraryLoadError& error) {
539 #if defined(OS_WIN)
540   // Only report load error code on Windows because that's the only platform
541   // that has a numerical error value.
542   std::string histogram_name =
543       std::string("Plugin.Ppapi") + (is_broker_ ? "Broker" : "Plugin") +
544       "LoadErrorCode_" + path.BaseName().MaybeAsASCII();
545
546   // For sparse histograms, we can use the macro, as it does not incorporate a
547   // static.
548   UMA_HISTOGRAM_SPARSE_SLOWLY(histogram_name, error.code);
549 #endif
550 }
551
552 }  // namespace content