Upstream version 5.34.104.0
[platform/framework/web/crosswalk.git] / src / components / nacl / renderer / ppb_nacl_private_impl.cc
1 // Copyright 2013 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 "components/nacl/renderer/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 "components/nacl/common/nacl_host_messages.h"
14 #include "components/nacl/common/nacl_types.h"
15 #include "components/nacl/renderer/pnacl_translation_resource_host.h"
16 #include "content/public/common/content_client.h"
17 #include "content/public/common/content_switches.h"
18 #include "content/public/common/sandbox_init.h"
19 #include "content/public/renderer/pepper_plugin_instance.h"
20 #include "content/public/renderer/render_thread.h"
21 #include "content/public/renderer/render_view.h"
22 #include "content/public/renderer/renderer_ppapi_host.h"
23 #include "ppapi/c/pp_bool.h"
24 #include "ppapi/c/private/pp_file_handle.h"
25 #include "ppapi/native_client/src/trusted/plugin/nacl_entry_points.h"
26 #include "ppapi/shared_impl/ppapi_globals.h"
27 #include "ppapi/shared_impl/ppapi_permissions.h"
28 #include "ppapi/shared_impl/ppapi_preferences.h"
29 #include "ppapi/shared_impl/var.h"
30 #include "ppapi/thunk/enter.h"
31 #include "third_party/WebKit/public/web/WebDOMResourceProgressEvent.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 #include "v8/include/v8.h"
38
39 namespace {
40
41 base::LazyInstance<scoped_refptr<PnaclTranslationResourceHost> >
42     g_pnacl_resource_host = LAZY_INSTANCE_INITIALIZER;
43
44 static bool InitializePnaclResourceHost() {
45   // Must run on the main thread.
46   content::RenderThread* render_thread = content::RenderThread::Get();
47   if (!render_thread)
48     return false;
49   if (!g_pnacl_resource_host.Get()) {
50     g_pnacl_resource_host.Get() = new PnaclTranslationResourceHost(
51         render_thread->GetIOMessageLoopProxy());
52     render_thread->AddFilter(g_pnacl_resource_host.Get());
53   }
54   return true;
55 }
56
57 struct InstanceInfo {
58   InstanceInfo() : plugin_pid(base::kNullProcessId), plugin_child_id(0) {}
59   GURL url;
60   ppapi::PpapiPermissions permissions;
61   base::ProcessId plugin_pid;
62   int plugin_child_id;
63   IPC::ChannelHandle channel_handle;
64 };
65
66 typedef std::map<PP_Instance, InstanceInfo> InstanceInfoMap;
67
68 base::LazyInstance<InstanceInfoMap> g_instance_info =
69     LAZY_INSTANCE_INITIALIZER;
70
71 static int GetRoutingID(PP_Instance instance) {
72   // Check that we are on the main renderer thread.
73   DCHECK(content::RenderThread::Get());
74   content::RendererPpapiHost *host =
75       content::RendererPpapiHost::GetForPPInstance(instance);
76   if (!host)
77     return 0;
78   return host->GetRoutingIDForWidget(instance);
79 }
80
81 // Launch NaCl's sel_ldr process.
82 void LaunchSelLdr(PP_Instance instance,
83                   const char* alleged_url,
84                   PP_Bool uses_irt,
85                   PP_Bool uses_ppapi,
86                   PP_Bool enable_ppapi_dev,
87                   PP_Bool enable_dyncode_syscalls,
88                   PP_Bool enable_exception_handling,
89                   PP_Bool enable_crash_throttling,
90                   void* imc_handle,
91                   struct PP_Var* error_message,
92                   PP_CompletionCallback callback) {
93   nacl::FileDescriptor result_socket;
94   IPC::Sender* sender = content::RenderThread::Get();
95   DCHECK(sender);
96   *error_message = PP_MakeUndefined();
97   int routing_id = 0;
98   // If the nexe uses ppapi APIs, we need a routing ID.
99   // To get the routing ID, we must be on the main thread.
100   // Some nexes do not use ppapi and launch from the background thread,
101   // so those nexes can skip finding a routing_id.
102   if (uses_ppapi) {
103     routing_id = GetRoutingID(instance);
104     if (!routing_id) {
105       ppapi::PpapiGlobals::Get()->GetMainThreadMessageLoop()->PostTask(
106           FROM_HERE,
107           base::Bind(callback.func, callback.user_data,
108                      static_cast<int32_t>(PP_ERROR_FAILED)));
109       return;
110     }
111   }
112
113   InstanceInfo instance_info;
114   instance_info.url = GURL(alleged_url);
115
116   uint32_t perm_bits = ppapi::PERMISSION_NONE;
117   // Conditionally block 'Dev' interfaces. We do this for the NaCl process, so
118   // it's clearer to developers when they are using 'Dev' inappropriately. We
119   // must also check on the trusted side of the proxy.
120   if (enable_ppapi_dev)
121     perm_bits |= ppapi::PERMISSION_DEV;
122   instance_info.permissions =
123       ppapi::PpapiPermissions::GetForCommandLine(perm_bits);
124   std::string error_message_string;
125   nacl::NaClLaunchResult launch_result;
126
127   if (!sender->Send(new NaClHostMsg_LaunchNaCl(
128           nacl::NaClLaunchParams(instance_info.url.spec(),
129                                  routing_id,
130                                  perm_bits,
131                                  PP_ToBool(uses_irt),
132                                  PP_ToBool(enable_dyncode_syscalls),
133                                  PP_ToBool(enable_exception_handling),
134                                  PP_ToBool(enable_crash_throttling)),
135           &launch_result,
136           &error_message_string))) {
137     ppapi::PpapiGlobals::Get()->GetMainThreadMessageLoop()->PostTask(
138         FROM_HERE,
139         base::Bind(callback.func, callback.user_data,
140                    static_cast<int32_t>(PP_ERROR_FAILED)));
141     return;
142   }
143   if (!error_message_string.empty()) {
144     *error_message = ppapi::StringVar::StringToPPVar(error_message_string);
145     ppapi::PpapiGlobals::Get()->GetMainThreadMessageLoop()->PostTask(
146         FROM_HERE,
147         base::Bind(callback.func, callback.user_data,
148                    static_cast<int32_t>(PP_ERROR_FAILED)));
149     return;
150   }
151   result_socket = launch_result.imc_channel_handle;
152   instance_info.channel_handle = launch_result.ipc_channel_handle;
153   instance_info.plugin_pid = launch_result.plugin_pid;
154   instance_info.plugin_child_id = launch_result.plugin_child_id;
155   // Don't save instance_info if channel handle is invalid.
156   bool invalid_handle = instance_info.channel_handle.name.empty();
157 #if defined(OS_POSIX)
158   if (!invalid_handle)
159     invalid_handle = (instance_info.channel_handle.socket.fd == -1);
160 #endif
161   if (!invalid_handle)
162     g_instance_info.Get()[instance] = instance_info;
163
164   *(static_cast<NaClHandle*>(imc_handle)) =
165       nacl::ToNativeHandle(result_socket);
166   ppapi::PpapiGlobals::Get()->GetMainThreadMessageLoop()->PostTask(
167       FROM_HERE,
168       base::Bind(callback.func, callback.user_data,
169                  static_cast<int32_t>(PP_OK)));
170 }
171
172 PP_ExternalPluginResult StartPpapiProxy(PP_Instance instance) {
173   InstanceInfoMap& map = g_instance_info.Get();
174   InstanceInfoMap::iterator it = map.find(instance);
175   if (it == map.end()) {
176     DLOG(ERROR) << "Could not find instance ID";
177     return PP_EXTERNAL_PLUGIN_FAILED;
178   }
179   InstanceInfo instance_info = it->second;
180   map.erase(it);
181
182   content::PepperPluginInstance* plugin_instance =
183       content::PepperPluginInstance::Get(instance);
184   if (!plugin_instance) {
185     DLOG(ERROR) << "GetInstance() failed";
186     return PP_EXTERNAL_PLUGIN_ERROR_MODULE;
187   }
188
189   return plugin_instance->SwitchToOutOfProcessProxy(
190       base::FilePath().AppendASCII(instance_info.url.spec()),
191       instance_info.permissions,
192       instance_info.channel_handle,
193       instance_info.plugin_pid,
194       instance_info.plugin_child_id);
195 }
196
197 int UrandomFD(void) {
198 #if defined(OS_POSIX)
199   return base::GetUrandomFD();
200 #else
201   return -1;
202 #endif
203 }
204
205 PP_Bool Are3DInterfacesDisabled() {
206   return PP_FromBool(CommandLine::ForCurrentProcess()->HasSwitch(
207                          switches::kDisable3DAPIs));
208 }
209
210 int32_t BrokerDuplicateHandle(PP_FileHandle source_handle,
211                               uint32_t process_id,
212                               PP_FileHandle* target_handle,
213                               uint32_t desired_access,
214                               uint32_t options) {
215 #if defined(OS_WIN)
216   return content::BrokerDuplicateHandle(source_handle, process_id,
217                                         target_handle, desired_access,
218                                         options);
219 #else
220   return 0;
221 #endif
222 }
223
224 PP_FileHandle GetReadonlyPnaclFD(const char* filename) {
225   IPC::PlatformFileForTransit out_fd = IPC::InvalidPlatformFileForTransit();
226   IPC::Sender* sender = content::RenderThread::Get();
227   DCHECK(sender);
228   if (!sender->Send(new NaClHostMsg_GetReadonlyPnaclFD(
229           std::string(filename),
230           &out_fd))) {
231     return base::kInvalidPlatformFileValue;
232   }
233   if (out_fd == IPC::InvalidPlatformFileForTransit()) {
234     return base::kInvalidPlatformFileValue;
235   }
236   base::PlatformFile handle =
237       IPC::PlatformFileForTransitToPlatformFile(out_fd);
238   return handle;
239 }
240
241 PP_FileHandle CreateTemporaryFile(PP_Instance instance) {
242   IPC::PlatformFileForTransit transit_fd = IPC::InvalidPlatformFileForTransit();
243   IPC::Sender* sender = content::RenderThread::Get();
244   DCHECK(sender);
245   if (!sender->Send(new NaClHostMsg_NaClCreateTemporaryFile(
246           &transit_fd))) {
247     return base::kInvalidPlatformFileValue;
248   }
249
250   if (transit_fd == IPC::InvalidPlatformFileForTransit()) {
251     return base::kInvalidPlatformFileValue;
252   }
253
254   base::PlatformFile handle = IPC::PlatformFileForTransitToPlatformFile(
255       transit_fd);
256   return handle;
257 }
258
259 int32_t GetNumberOfProcessors() {
260   int32_t num_processors;
261   IPC::Sender* sender = content::RenderThread::Get();
262   DCHECK(sender);
263   if(!sender->Send(new NaClHostMsg_NaClGetNumProcessors(&num_processors))) {
264     return 1;
265   }
266   return num_processors;
267 }
268
269 int32_t GetNexeFd(PP_Instance instance,
270                   const char* pexe_url,
271                   uint32_t abi_version,
272                   uint32_t opt_level,
273                   const char* last_modified,
274                   const char* etag,
275                   PP_Bool has_no_store_header,
276                   const char* sandbox_isa,
277                   const char* extra_flags,
278                   PP_Bool* is_hit,
279                   PP_FileHandle* handle,
280                   struct PP_CompletionCallback callback) {
281   ppapi::thunk::EnterInstance enter(instance, callback);
282   if (enter.failed())
283     return enter.retval();
284   if (!pexe_url || !last_modified || !etag || !is_hit || !handle)
285     return enter.SetResult(PP_ERROR_BADARGUMENT);
286   if (!InitializePnaclResourceHost())
287     return enter.SetResult(PP_ERROR_FAILED);
288
289   base::Time last_modified_time;
290   // If FromString fails, it doesn't touch last_modified_time and we just send
291   // the default-constructed null value.
292   base::Time::FromString(last_modified, &last_modified_time);
293
294   nacl::PnaclCacheInfo cache_info;
295   cache_info.pexe_url = GURL(pexe_url);
296   cache_info.abi_version = abi_version;
297   cache_info.opt_level = opt_level;
298   cache_info.last_modified = last_modified_time;
299   cache_info.etag = std::string(etag);
300   cache_info.has_no_store_header = PP_ToBool(has_no_store_header);
301   cache_info.sandbox_isa = std::string(sandbox_isa);
302   cache_info.extra_flags = std::string(extra_flags);
303
304   g_pnacl_resource_host.Get()->RequestNexeFd(
305       GetRoutingID(instance),
306       instance,
307       cache_info,
308       is_hit,
309       handle,
310       enter.callback());
311
312   return enter.SetResult(PP_OK_COMPLETIONPENDING);
313 }
314
315 void ReportTranslationFinished(PP_Instance instance, PP_Bool success) {
316   // If the resource host isn't initialized, don't try to do that here.
317   // Just return because something is already very wrong.
318   if (g_pnacl_resource_host.Get() == NULL)
319     return;
320   g_pnacl_resource_host.Get()->ReportTranslationFinished(instance, success);
321 }
322
323 PP_ExternalPluginResult ReportNaClError(PP_Instance instance,
324                               PP_NaClError error_id) {
325   IPC::Sender* sender = content::RenderThread::Get();
326
327   if (!sender->Send(
328           new NaClHostMsg_NaClErrorStatus(
329               // TODO(dschuff): does this enum need to be sent as an int,
330               // or is it safe to include the appropriate headers in
331               // render_messages.h?
332               GetRoutingID(instance), static_cast<int>(error_id)))) {
333     return PP_EXTERNAL_PLUGIN_FAILED;
334   }
335   return PP_EXTERNAL_PLUGIN_OK;
336 }
337
338 PP_FileHandle OpenNaClExecutable(PP_Instance instance,
339                                  const char* file_url,
340                                  uint64_t* nonce_lo,
341                                  uint64_t* nonce_hi) {
342   IPC::PlatformFileForTransit out_fd = IPC::InvalidPlatformFileForTransit();
343   IPC::Sender* sender = content::RenderThread::Get();
344   DCHECK(sender);
345   *nonce_lo = 0;
346   *nonce_hi = 0;
347   base::FilePath file_path;
348   if (!sender->Send(
349       new NaClHostMsg_OpenNaClExecutable(GetRoutingID(instance),
350                                                GURL(file_url),
351                                                &out_fd,
352                                                nonce_lo,
353                                                nonce_hi))) {
354     return base::kInvalidPlatformFileValue;
355   }
356
357   if (out_fd == IPC::InvalidPlatformFileForTransit()) {
358     return base::kInvalidPlatformFileValue;
359   }
360
361   base::PlatformFile handle =
362       IPC::PlatformFileForTransitToPlatformFile(out_fd);
363   return handle;
364 }
365
366 blink::WebString EventTypeToString(PP_NaClEventType event_type) {
367   switch (event_type) {
368     case PP_NACL_EVENT_LOADSTART:
369       return blink::WebString::fromUTF8("loadstart");
370     case PP_NACL_EVENT_PROGRESS:
371       return blink::WebString::fromUTF8("progress");
372     case PP_NACL_EVENT_ERROR:
373       return blink::WebString::fromUTF8("error");
374     case PP_NACL_EVENT_ABORT:
375       return blink::WebString::fromUTF8("abort");
376     case PP_NACL_EVENT_LOAD:
377       return blink::WebString::fromUTF8("load");
378     case PP_NACL_EVENT_LOADEND:
379       return blink::WebString::fromUTF8("loadend");
380     case PP_NACL_EVENT_CRASH:
381       return blink::WebString::fromUTF8("crash");
382   }
383   NOTIMPLEMENTED();
384   return blink::WebString();
385 }
386
387 struct ProgressEvent {
388   PP_Instance instance;
389   PP_NaClEventType event_type;
390   std::string resource_url;
391   bool length_is_computable;
392   uint64_t loaded_bytes;
393   uint64_t total_bytes;
394 };
395
396 void DispatchEventOnMainThread(const ProgressEvent &event);
397
398 void DispatchEvent(PP_Instance instance,
399                    PP_NaClEventType event_type,
400                    const char *resource_url,
401                    PP_Bool length_is_computable,
402                    uint64_t loaded_bytes,
403                    uint64_t total_bytes) {
404   ProgressEvent p;
405   p.instance = instance;
406   p.event_type = event_type;
407   p.length_is_computable = PP_ToBool(length_is_computable);
408   p.loaded_bytes = loaded_bytes;
409   p.total_bytes = total_bytes;
410
411   // We have to copy resource_url into our struct manually since we don't have
412   // guarantees about the PP_Var lifetime.
413   p.resource_url = std::string();
414   if (resource_url)
415     p.resource_url = std::string(resource_url);
416
417   ppapi::PpapiGlobals::Get()->GetMainThreadMessageLoop()->PostTask(
418       FROM_HERE,
419       base::Bind(&DispatchEventOnMainThread, p));
420 }
421
422 void DispatchEventOnMainThread(const ProgressEvent &event) {
423   content::PepperPluginInstance* plugin_instance =
424       content::PepperPluginInstance::Get(event.instance);
425   // The instance may have been destroyed after we were scheduled, so just
426   // return if it's gone.
427   if (!plugin_instance)
428     return;
429
430   blink::WebPluginContainer* container = plugin_instance->GetContainer();
431   // It's possible that container() is NULL if the plugin has been removed from
432   // the DOM (but the PluginInstance is not destroyed yet).
433   if (!container)
434     return;
435   blink::WebFrame* frame = container->element().document().frame();
436   if (!frame)
437     return;
438   v8::HandleScope handle_scope(plugin_instance->GetIsolate());
439   v8::Local<v8::Context> context(
440       plugin_instance->GetIsolate()->GetCurrentContext());
441   if (context.IsEmpty()) {
442     // If there's no JavaScript on the stack, we have to make a new Context.
443     context = v8::Context::New(plugin_instance->GetIsolate());
444   }
445   v8::Context::Scope context_scope(context);
446
447   if (!event.resource_url.empty()) {
448     blink::WebString url_string = blink::WebString::fromUTF8(
449         event.resource_url.data(), event.resource_url.size());
450     blink::WebDOMResourceProgressEvent blink_event(
451         EventTypeToString(event.event_type),
452         event.length_is_computable,
453         event.loaded_bytes,
454         event.total_bytes,
455         url_string);
456     container->element().dispatchEvent(blink_event);
457   } else {
458     blink::WebDOMProgressEvent blink_event(EventTypeToString(event.event_type),
459                                            event.length_is_computable,
460                                            event.loaded_bytes,
461                                            event.total_bytes);
462     container->element().dispatchEvent(blink_event);
463   }
464 }
465
466 void SetReadOnlyProperty(PP_Instance instance,
467                          struct PP_Var key,
468                          struct PP_Var value) {
469   content::PepperPluginInstance* plugin_instance =
470       content::PepperPluginInstance::Get(instance);
471   plugin_instance->SetEmbedProperty(key, value);
472 }
473
474 const PPB_NaCl_Private nacl_interface = {
475   &LaunchSelLdr,
476   &StartPpapiProxy,
477   &UrandomFD,
478   &Are3DInterfacesDisabled,
479   &BrokerDuplicateHandle,
480   &GetReadonlyPnaclFD,
481   &CreateTemporaryFile,
482   &GetNumberOfProcessors,
483   &GetNexeFd,
484   &ReportTranslationFinished,
485   &ReportNaClError,
486   &OpenNaClExecutable,
487   &DispatchEvent,
488   &SetReadOnlyProperty
489 };
490
491 }  // namespace
492
493 namespace nacl {
494
495 const PPB_NaCl_Private* GetNaClPrivateInterface() {
496   return &nacl_interface;
497 }
498
499 }  // namespace nacl
500
501 #endif  // DISABLE_NACL