Upstream version 7.35.139.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/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"
46
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
54
55 #if defined(OS_WIN)
56 extern sandbox::TargetServices* g_target_services;
57
58 // Used by EnumSystemLocales for warming up.
59 static BOOL CALLBACK EnumLocalesProc(LPTSTR lpLocaleString) {
60   return TRUE;
61 }
62
63 static BOOL CALLBACK EnumLocalesProcEx(
64     LPWSTR lpLocaleString,
65     DWORD dwFlags,
66     LPARAM lParam) {
67   return TRUE;
68 }
69
70 // Warm up language subsystems before the sandbox is turned on.
71 static void WarmupWindowsLocales(const ppapi::PpapiPermissions& permissions) {
72   ::GetUserDefaultLangID();
73   ::GetUserDefaultLCID();
74
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);
79
80       HMODULE handle_kern32 = GetModuleHandleW(L"Kernel32.dll");
81       PfnEnumSystemLocalesEx enum_sys_locales_ex =
82           reinterpret_cast<PfnEnumSystemLocalesEx>
83               (GetProcAddress(handle_kern32, "EnumSystemLocalesEx"));
84
85       enum_sys_locales_ex(EnumLocalesProcEx, LOCALE_WINDOWS, 0, 0);
86     } else {
87       EnumSystemLocalesW(EnumLocalesProc, LCID_INSTALLED);
88     }
89   }
90 }
91 #else
92 extern void* g_target_services;
93 #endif
94
95 namespace content {
96
97 typedef int32_t (*InitializeBrokerFunc)
98     (PP_ConnectInstance_Func* connect_instance_func);
99
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));
110
111   webkit_platform_support_.reset(new PpapiWebKitPlatformSupportImpl);
112   blink::initialize(webkit_platform_support_.get());
113
114   if (!is_broker_) {
115     channel()->AddFilter(
116         new ppapi::proxy::PluginMessageFilter(
117             NULL, globals->resource_reply_thread_registrar()));
118   }
119 }
120
121 PpapiThread::~PpapiThread() {
122 }
123
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();
130 }
131
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);
136
137   return sync_message_filter()->Send(msg);
138 }
139
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;
154 }
155
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
162 }
163
164 base::MessageLoopProxy* PpapiThread::GetIPCMessageLoop() {
165   return ChildProcess::current()->io_message_loop_proxy();
166 }
167
168 base::WaitableEvent* PpapiThread::GetShutdownEvent() {
169   return ChildProcess::current()->GetShutDownEvent();
170 }
171
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);
181   }
182 #endif
183
184   DCHECK(peer_pid != base::kNullProcessId);
185   return BrokerGetFileHandleForProcess(handle, peer_pid, should_close_source);
186 }
187
188 std::set<PP_Instance>* PpapiThread::GetGloballySeenInstanceIDSet() {
189   return &globally_seen_instance_ids_;
190 }
191
192 IPC::Sender* PpapiThread::GetBrowserSender() {
193   return this;
194 }
195
196 std::string PpapiThread::GetUILanguage() {
197   CommandLine* command_line = CommandLine::ForCurrentProcess();
198   return command_line->GetSwitchValueASCII(switches::kLang);
199 }
200
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
206 }
207
208 void PpapiThread::SetActiveURL(const std::string& url) {
209   GetContentClient()->SetActiveURL(GURL(url));
210 }
211
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();
221 }
222
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;
227   }
228
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;
238 }
239
240 void PpapiThread::Unregister(uint32 plugin_dispatcher_id) {
241   plugin_dispatchers_.erase(plugin_dispatcher_id);
242 }
243
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());
249
250   SavePluginName(path);
251
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;
256
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;
266     }
267   }
268
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;
282     }
283
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;
292     }
293
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"));
301
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;
311       }
312     }
313   }
314
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();
319
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");
329       }
330
331       if (base::win::OSInfo::GetInstance()->version() >=
332           base::win::VERSION_WIN7) {
333         base::CPU cpu;
334         if ((cpu.vendor_name() == "AuthenticAMD") && (cpu.family() > 0x14)) {
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
341         }
342       }
343     }
344
345     // Cause advapi32 to load before the sandbox is turned on.
346     unsigned int dummy_rand;
347     rand_s(&dummy_rand);
348
349     WarmupWindowsLocales(permissions);
350
351     g_target_services->LowerToken();
352   }
353 #endif
354
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;
364     }
365
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;
371     }
372     if (!connect_instance_func_) {
373       LOG(WARNING) << "InitBroker did not provide PP_ConnectInstance_Func";
374       ReportLoadResult(path, INIT_FAILED);
375       return;
376     }
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
384
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;
392     }
393   }
394
395   // Initialization succeeded, so keep the plugin DLL loaded.
396   library_.Reset(library.Release());
397
398   ReportLoadResult(path, LOAD_SUCCESS);
399 }
400
401 void PpapiThread::OnCreateChannel(base::ProcessId renderer_pid,
402                                   int renderer_child_id,
403                                   bool incognito) {
404   IPC::ChannelHandle channel_handle;
405
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;
411   }
412
413   Send(new PpapiHostMsg_ChannelCreated(channel_handle));
414 }
415
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));
426 }
427
428 void PpapiThread::OnCrash() {
429   // Intentionally crash upon the request of the browser.
430   volatile int* null_pointer = NULL;
431   *null_pointer = 0;
432 }
433
434 void PpapiThread::OnHang() {
435   // Intentionally hang upon the request of the browser.
436   for (;;)
437     base::PlatformThread::Sleep(base::TimeDelta::FromSeconds(1));
438 }
439
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));
449
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;
471   }
472
473   if (!init_result) {
474     delete dispatcher;
475     return false;
476   }
477
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
487
488   // From here, the dispatcher will manage its own lifetime according to the
489   // lifetime of the attached channel.
490   return true;
491 }
492
493 void PpapiThread::SavePluginName(const base::FilePath& path) {
494   ppapi::proxy::PluginGlobals::Get()->set_plugin_name(
495       path.BaseName().AsUTF8Unsafe());
496
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());
502   }
503 }
504
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();
511
512   // Note: This leaks memory, which is expected behavior.
513   base::HistogramBase* histogram =
514       base::LinearHistogram::FactoryGet(
515           histogram_name,
516           1,
517           LOAD_RESULT_MAX,
518           LOAD_RESULT_MAX + 1,
519           base::HistogramBase::kUmaTargetedHistogramFlag);
520
521   histogram->Add(result);
522 }
523
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();
533
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
538 }
539
540 }  // namespace content