- add sources.
[platform/framework/web/crosswalk.git] / src / chrome / renderer / pepper / ppb_nacl_private_impl.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 "chrome/renderer/pepper/ppb_nacl_private_impl.h"
6
7 #ifndef DISABLE_NACL
8
9 #include "base/command_line.h"
10 #include "base/lazy_instance.h"
11 #include "base/logging.h"
12 #include "base/rand_util.h"
13 #include "chrome/common/chrome_switches.h"
14 #include "chrome/renderer/chrome_render_process_observer.h"
15 #include "chrome/renderer/pepper/pnacl_translation_resource_host.h"
16 #include "components/nacl/common/nacl_host_messages.h"
17 #include "components/nacl/common/nacl_types.h"
18 #include "content/public/common/content_client.h"
19 #include "content/public/common/content_switches.h"
20 #include "content/public/common/sandbox_init.h"
21 #include "content/public/renderer/pepper_plugin_instance.h"
22 #include "content/public/renderer/renderer_ppapi_host.h"
23 #include "content/public/renderer/render_thread.h"
24 #include "content/public/renderer/render_view.h"
25 #include "ppapi/c/pp_bool.h"
26 #include "ppapi/c/private/pp_file_handle.h"
27 #include "ppapi/native_client/src/trusted/plugin/nacl_entry_points.h"
28 #include "ppapi/shared_impl/ppapi_permissions.h"
29 #include "ppapi/shared_impl/ppapi_preferences.h"
30 #include "ppapi/shared_impl/var.h"
31 #include "ppapi/thunk/enter.h"
32 #include "third_party/WebKit/public/web/WebDocument.h"
33 #include "third_party/WebKit/public/web/WebElement.h"
34 #include "third_party/WebKit/public/web/WebFrame.h"
35 #include "third_party/WebKit/public/web/WebPluginContainer.h"
36 #include "third_party/WebKit/public/web/WebView.h"
37
38 namespace {
39
40 base::LazyInstance<scoped_refptr<PnaclTranslationResourceHost> >
41     g_pnacl_resource_host = LAZY_INSTANCE_INITIALIZER;
42
43 static bool InitializePnaclResourceHost() {
44   // Must run on the main thread.
45   content::RenderThread* render_thread = content::RenderThread::Get();
46   if (!render_thread)
47     return false;
48   if (!g_pnacl_resource_host.Get()) {
49     g_pnacl_resource_host.Get() = new PnaclTranslationResourceHost(
50         render_thread->GetIOMessageLoopProxy());
51     render_thread->AddFilter(g_pnacl_resource_host.Get());
52   }
53   return true;
54 }
55
56 struct InstanceInfo {
57   InstanceInfo() : plugin_pid(base::kNullProcessId), plugin_child_id(0) {}
58   GURL url;
59   ppapi::PpapiPermissions permissions;
60   base::ProcessId plugin_pid;
61   int plugin_child_id;
62   IPC::ChannelHandle channel_handle;
63 };
64
65 typedef std::map<PP_Instance, InstanceInfo> InstanceInfoMap;
66
67 base::LazyInstance<InstanceInfoMap> g_instance_info =
68     LAZY_INSTANCE_INITIALIZER;
69
70 static int GetRoutingID(PP_Instance instance) {
71   // Check that we are on the main renderer thread.
72   DCHECK(content::RenderThread::Get());
73   content::RendererPpapiHost *host =
74       content::RendererPpapiHost::GetForPPInstance(instance);
75   if (!host)
76     return 0;
77   return host->GetRoutingIDForWidget(instance);
78 }
79
80 // Launch NaCl's sel_ldr process.
81 PP_ExternalPluginResult LaunchSelLdr(PP_Instance instance,
82                            const char* alleged_url,
83                            PP_Bool uses_irt,
84                            PP_Bool uses_ppapi,
85                            PP_Bool enable_ppapi_dev,
86                            PP_Bool enable_dyncode_syscalls,
87                            PP_Bool enable_exception_handling,
88                            PP_Bool enable_crash_throttling,
89                            void* imc_handle,
90                            struct PP_Var* error_message) {
91   nacl::FileDescriptor result_socket;
92   IPC::Sender* sender = content::RenderThread::Get();
93   DCHECK(sender);
94   *error_message = PP_MakeUndefined();
95   int routing_id = 0;
96   // If the nexe uses ppapi APIs, we need a routing ID.
97   // To get the routing ID, we must be on the main thread.
98   // Some nexes do not use ppapi and launch from the background thread,
99   // so those nexes can skip finding a routing_id.
100   if (uses_ppapi) {
101     routing_id = GetRoutingID(instance);
102     if (!routing_id)
103       return PP_EXTERNAL_PLUGIN_FAILED;
104   }
105
106   InstanceInfo instance_info;
107   instance_info.url = GURL(alleged_url);
108
109   uint32_t perm_bits = ppapi::PERMISSION_NONE;
110   // Conditionally block 'Dev' interfaces. We do this for the NaCl process, so
111   // it's clearer to developers when they are using 'Dev' inappropriately. We
112   // must also check on the trusted side of the proxy.
113   if (enable_ppapi_dev)
114     perm_bits |= ppapi::PERMISSION_DEV;
115   instance_info.permissions =
116       ppapi::PpapiPermissions::GetForCommandLine(perm_bits);
117   std::string error_message_string;
118   nacl::NaClLaunchResult launch_result;
119
120   if (!sender->Send(new NaClHostMsg_LaunchNaCl(
121           nacl::NaClLaunchParams(instance_info.url.spec(),
122                                  routing_id,
123                                  perm_bits,
124                                  PP_ToBool(uses_irt),
125                                  PP_ToBool(enable_dyncode_syscalls),
126                                  PP_ToBool(enable_exception_handling),
127                                  PP_ToBool(enable_crash_throttling)),
128           &launch_result,
129           &error_message_string))) {
130     return PP_EXTERNAL_PLUGIN_FAILED;
131   }
132   if (!error_message_string.empty()) {
133     *error_message = ppapi::StringVar::StringToPPVar(error_message_string);
134     return PP_EXTERNAL_PLUGIN_FAILED;
135   }
136   result_socket = launch_result.imc_channel_handle;
137   instance_info.channel_handle = launch_result.ipc_channel_handle;
138   instance_info.plugin_pid = launch_result.plugin_pid;
139   instance_info.plugin_child_id = launch_result.plugin_child_id;
140   // Don't save instance_info if channel handle is invalid.
141   bool invalid_handle = instance_info.channel_handle.name.empty();
142 #if defined(OS_POSIX)
143   if (!invalid_handle)
144     invalid_handle = (instance_info.channel_handle.socket.fd == -1);
145 #endif
146   if (!invalid_handle)
147     g_instance_info.Get()[instance] = instance_info;
148
149   *(static_cast<NaClHandle*>(imc_handle)) =
150       nacl::ToNativeHandle(result_socket);
151
152   return PP_EXTERNAL_PLUGIN_OK;
153 }
154
155 PP_ExternalPluginResult StartPpapiProxy(PP_Instance instance) {
156   InstanceInfoMap& map = g_instance_info.Get();
157   InstanceInfoMap::iterator it = map.find(instance);
158   if (it == map.end()) {
159     DLOG(ERROR) << "Could not find instance ID";
160     return PP_EXTERNAL_PLUGIN_FAILED;
161   }
162   InstanceInfo instance_info = it->second;
163   map.erase(it);
164
165   content::PepperPluginInstance* plugin_instance =
166       content::PepperPluginInstance::Get(instance);
167   if (!plugin_instance) {
168     DLOG(ERROR) << "GetInstance() failed";
169     return PP_EXTERNAL_PLUGIN_ERROR_MODULE;
170   }
171
172   return plugin_instance->SwitchToOutOfProcessProxy(
173       base::FilePath().AppendASCII(instance_info.url.spec()),
174       instance_info.permissions,
175       instance_info.channel_handle,
176       instance_info.plugin_pid,
177       instance_info.plugin_child_id);
178 }
179
180 int UrandomFD(void) {
181 #if defined(OS_POSIX)
182   return base::GetUrandomFD();
183 #else
184   return -1;
185 #endif
186 }
187
188 PP_Bool Are3DInterfacesDisabled() {
189   return PP_FromBool(CommandLine::ForCurrentProcess()->HasSwitch(
190                          switches::kDisable3DAPIs));
191 }
192
193 int32_t BrokerDuplicateHandle(PP_FileHandle source_handle,
194                               uint32_t process_id,
195                               PP_FileHandle* target_handle,
196                               uint32_t desired_access,
197                               uint32_t options) {
198 #if defined(OS_WIN)
199   return content::BrokerDuplicateHandle(source_handle, process_id,
200                                         target_handle, desired_access,
201                                         options);
202 #else
203   return 0;
204 #endif
205 }
206
207 PP_FileHandle GetReadonlyPnaclFD(const char* filename) {
208   IPC::PlatformFileForTransit out_fd = IPC::InvalidPlatformFileForTransit();
209   IPC::Sender* sender = content::RenderThread::Get();
210   DCHECK(sender);
211   if (!sender->Send(new NaClHostMsg_GetReadonlyPnaclFD(
212           std::string(filename),
213           &out_fd))) {
214     return base::kInvalidPlatformFileValue;
215   }
216   if (out_fd == IPC::InvalidPlatformFileForTransit()) {
217     return base::kInvalidPlatformFileValue;
218   }
219   base::PlatformFile handle =
220       IPC::PlatformFileForTransitToPlatformFile(out_fd);
221   return handle;
222 }
223
224 PP_FileHandle CreateTemporaryFile(PP_Instance instance) {
225   IPC::PlatformFileForTransit transit_fd = IPC::InvalidPlatformFileForTransit();
226   IPC::Sender* sender = content::RenderThread::Get();
227   DCHECK(sender);
228   if (!sender->Send(new NaClHostMsg_NaClCreateTemporaryFile(
229           &transit_fd))) {
230     return base::kInvalidPlatformFileValue;
231   }
232
233   if (transit_fd == IPC::InvalidPlatformFileForTransit()) {
234     return base::kInvalidPlatformFileValue;
235   }
236
237   base::PlatformFile handle = IPC::PlatformFileForTransitToPlatformFile(
238       transit_fd);
239   return handle;
240 }
241
242 int32_t GetNexeFd(PP_Instance instance,
243                   const char* pexe_url,
244                   uint32_t abi_version,
245                   uint32_t opt_level,
246                   const char* last_modified,
247                   const char* etag,
248                   PP_Bool has_no_store_header,
249                   PP_Bool* is_hit,
250                   PP_FileHandle* handle,
251                   struct PP_CompletionCallback callback) {
252   ppapi::thunk::EnterInstance enter(instance, callback);
253   if (enter.failed())
254     return enter.retval();
255   if (!pexe_url || !last_modified || !etag || !is_hit || !handle)
256     return enter.SetResult(PP_ERROR_BADARGUMENT);
257   if (!InitializePnaclResourceHost())
258     return enter.SetResult(PP_ERROR_FAILED);
259
260   base::Time last_modified_time;
261   // If FromString fails, it doesn't touch last_modified_time and we just send
262   // the default-constructed null value.
263   base::Time::FromString(last_modified, &last_modified_time);
264
265   nacl::PnaclCacheInfo cache_info;
266   cache_info.pexe_url = GURL(pexe_url);
267   cache_info.abi_version = abi_version;
268   cache_info.opt_level = opt_level;
269   cache_info.last_modified = last_modified_time;
270   cache_info.etag = std::string(etag);
271   cache_info.has_no_store_header = PP_ToBool(has_no_store_header);
272
273   g_pnacl_resource_host.Get()->RequestNexeFd(
274       GetRoutingID(instance),
275       instance,
276       cache_info,
277       is_hit,
278       handle,
279       enter.callback());
280
281   return enter.SetResult(PP_OK_COMPLETIONPENDING);
282 }
283
284 void ReportTranslationFinished(PP_Instance instance, PP_Bool success) {
285   // If the resource host isn't initialized, don't try to do that here.
286   // Just return because something is already very wrong.
287   if (g_pnacl_resource_host.Get() == NULL)
288     return;
289   g_pnacl_resource_host.Get()->ReportTranslationFinished(instance, success);
290 }
291
292 PP_Bool IsOffTheRecord() {
293   return PP_FromBool(ChromeRenderProcessObserver::is_incognito_process());
294 }
295
296 PP_ExternalPluginResult ReportNaClError(PP_Instance instance,
297                               PP_NaClError error_id) {
298   IPC::Sender* sender = content::RenderThread::Get();
299
300   if (!sender->Send(
301           new NaClHostMsg_NaClErrorStatus(
302               // TODO(dschuff): does this enum need to be sent as an int,
303               // or is it safe to include the appropriate headers in
304               // render_messages.h?
305               GetRoutingID(instance), static_cast<int>(error_id)))) {
306     return PP_EXTERNAL_PLUGIN_FAILED;
307   }
308   return PP_EXTERNAL_PLUGIN_OK;
309 }
310
311 PP_FileHandle OpenNaClExecutable(PP_Instance instance,
312                                  const char* file_url,
313                                  uint64_t* nonce_lo,
314                                  uint64_t* nonce_hi) {
315   IPC::PlatformFileForTransit out_fd = IPC::InvalidPlatformFileForTransit();
316   IPC::Sender* sender = content::RenderThread::Get();
317   DCHECK(sender);
318   *nonce_lo = 0;
319   *nonce_hi = 0;
320   base::FilePath file_path;
321   if (!sender->Send(
322       new NaClHostMsg_OpenNaClExecutable(GetRoutingID(instance),
323                                                GURL(file_url),
324                                                &out_fd,
325                                                nonce_lo,
326                                                nonce_hi))) {
327     return base::kInvalidPlatformFileValue;
328   }
329
330   if (out_fd == IPC::InvalidPlatformFileForTransit()) {
331     return base::kInvalidPlatformFileValue;
332   }
333
334   base::PlatformFile handle =
335       IPC::PlatformFileForTransitToPlatformFile(out_fd);
336   return handle;
337 }
338
339 const PPB_NaCl_Private nacl_interface = {
340   &LaunchSelLdr,
341   &StartPpapiProxy,
342   &UrandomFD,
343   &Are3DInterfacesDisabled,
344   &BrokerDuplicateHandle,
345   &GetReadonlyPnaclFD,
346   &CreateTemporaryFile,
347   &GetNexeFd,
348   &ReportTranslationFinished,
349   &IsOffTheRecord,
350   &ReportNaClError,
351   &OpenNaClExecutable
352 };
353
354 }  // namespace
355
356 const PPB_NaCl_Private* PPB_NaCl_Private_Impl::GetInterface() {
357   return &nacl_interface;
358 }
359
360 #endif  // DISABLE_NACL