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.
5 #include "components/nacl/renderer/ppb_nacl_private_impl.h"
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"
41 base::LazyInstance<scoped_refptr<PnaclTranslationResourceHost> >
42 g_pnacl_resource_host = LAZY_INSTANCE_INITIALIZER;
44 static bool InitializePnaclResourceHost() {
45 // Must run on the main thread.
46 content::RenderThread* render_thread = content::RenderThread::Get();
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());
58 InstanceInfo() : plugin_pid(base::kNullProcessId), plugin_child_id(0) {}
60 ppapi::PpapiPermissions permissions;
61 base::ProcessId plugin_pid;
63 IPC::ChannelHandle channel_handle;
66 typedef std::map<PP_Instance, InstanceInfo> InstanceInfoMap;
68 base::LazyInstance<InstanceInfoMap> g_instance_info =
69 LAZY_INSTANCE_INITIALIZER;
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);
78 return host->GetRoutingIDForWidget(instance);
81 // Launch NaCl's sel_ldr process.
82 void LaunchSelLdr(PP_Instance instance,
83 const char* alleged_url,
86 PP_Bool enable_ppapi_dev,
87 PP_Bool enable_dyncode_syscalls,
88 PP_Bool enable_exception_handling,
89 PP_Bool enable_crash_throttling,
91 struct PP_Var* error_message,
92 PP_CompletionCallback callback) {
93 nacl::FileDescriptor result_socket;
94 IPC::Sender* sender = content::RenderThread::Get();
96 *error_message = PP_MakeUndefined();
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.
103 routing_id = GetRoutingID(instance);
105 ppapi::PpapiGlobals::Get()->GetMainThreadMessageLoop()->PostTask(
107 base::Bind(callback.func, callback.user_data,
108 static_cast<int32_t>(PP_ERROR_FAILED)));
113 InstanceInfo instance_info;
114 instance_info.url = GURL(alleged_url);
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;
127 if (!sender->Send(new NaClHostMsg_LaunchNaCl(
128 nacl::NaClLaunchParams(instance_info.url.spec(),
132 PP_ToBool(enable_dyncode_syscalls),
133 PP_ToBool(enable_exception_handling),
134 PP_ToBool(enable_crash_throttling)),
136 &error_message_string))) {
137 ppapi::PpapiGlobals::Get()->GetMainThreadMessageLoop()->PostTask(
139 base::Bind(callback.func, callback.user_data,
140 static_cast<int32_t>(PP_ERROR_FAILED)));
143 if (!error_message_string.empty()) {
144 *error_message = ppapi::StringVar::StringToPPVar(error_message_string);
145 ppapi::PpapiGlobals::Get()->GetMainThreadMessageLoop()->PostTask(
147 base::Bind(callback.func, callback.user_data,
148 static_cast<int32_t>(PP_ERROR_FAILED)));
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)
159 invalid_handle = (instance_info.channel_handle.socket.fd == -1);
162 g_instance_info.Get()[instance] = instance_info;
164 *(static_cast<NaClHandle*>(imc_handle)) =
165 nacl::ToNativeHandle(result_socket);
166 ppapi::PpapiGlobals::Get()->GetMainThreadMessageLoop()->PostTask(
168 base::Bind(callback.func, callback.user_data,
169 static_cast<int32_t>(PP_OK)));
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;
179 InstanceInfo instance_info = it->second;
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;
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);
197 int UrandomFD(void) {
198 #if defined(OS_POSIX)
199 return base::GetUrandomFD();
205 PP_Bool Are3DInterfacesDisabled() {
206 return PP_FromBool(CommandLine::ForCurrentProcess()->HasSwitch(
207 switches::kDisable3DAPIs));
210 int32_t BrokerDuplicateHandle(PP_FileHandle source_handle,
212 PP_FileHandle* target_handle,
213 uint32_t desired_access,
216 return content::BrokerDuplicateHandle(source_handle, process_id,
217 target_handle, desired_access,
224 PP_FileHandle GetReadonlyPnaclFD(const char* filename) {
225 IPC::PlatformFileForTransit out_fd = IPC::InvalidPlatformFileForTransit();
226 IPC::Sender* sender = content::RenderThread::Get();
228 if (!sender->Send(new NaClHostMsg_GetReadonlyPnaclFD(
229 std::string(filename),
231 return base::kInvalidPlatformFileValue;
233 if (out_fd == IPC::InvalidPlatformFileForTransit()) {
234 return base::kInvalidPlatformFileValue;
236 base::PlatformFile handle =
237 IPC::PlatformFileForTransitToPlatformFile(out_fd);
241 PP_FileHandle CreateTemporaryFile(PP_Instance instance) {
242 IPC::PlatformFileForTransit transit_fd = IPC::InvalidPlatformFileForTransit();
243 IPC::Sender* sender = content::RenderThread::Get();
245 if (!sender->Send(new NaClHostMsg_NaClCreateTemporaryFile(
247 return base::kInvalidPlatformFileValue;
250 if (transit_fd == IPC::InvalidPlatformFileForTransit()) {
251 return base::kInvalidPlatformFileValue;
254 base::PlatformFile handle = IPC::PlatformFileForTransitToPlatformFile(
259 int32_t GetNumberOfProcessors() {
260 int32_t num_processors;
261 IPC::Sender* sender = content::RenderThread::Get();
263 if(!sender->Send(new NaClHostMsg_NaClGetNumProcessors(&num_processors))) {
266 return num_processors;
269 int32_t GetNexeFd(PP_Instance instance,
270 const char* pexe_url,
271 uint32_t abi_version,
273 const char* last_modified,
275 PP_Bool has_no_store_header,
276 const char* sandbox_isa,
277 const char* extra_flags,
279 PP_FileHandle* handle,
280 struct PP_CompletionCallback callback) {
281 ppapi::thunk::EnterInstance enter(instance, callback);
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);
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);
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);
304 g_pnacl_resource_host.Get()->RequestNexeFd(
305 GetRoutingID(instance),
312 return enter.SetResult(PP_OK_COMPLETIONPENDING);
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)
320 g_pnacl_resource_host.Get()->ReportTranslationFinished(instance, success);
323 PP_ExternalPluginResult ReportNaClError(PP_Instance instance,
324 PP_NaClError error_id) {
325 IPC::Sender* sender = content::RenderThread::Get();
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;
335 return PP_EXTERNAL_PLUGIN_OK;
338 PP_FileHandle OpenNaClExecutable(PP_Instance instance,
339 const char* file_url,
341 uint64_t* nonce_hi) {
342 IPC::PlatformFileForTransit out_fd = IPC::InvalidPlatformFileForTransit();
343 IPC::Sender* sender = content::RenderThread::Get();
347 base::FilePath file_path;
349 new NaClHostMsg_OpenNaClExecutable(GetRoutingID(instance),
354 return base::kInvalidPlatformFileValue;
357 if (out_fd == IPC::InvalidPlatformFileForTransit()) {
358 return base::kInvalidPlatformFileValue;
361 base::PlatformFile handle =
362 IPC::PlatformFileForTransitToPlatformFile(out_fd);
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");
384 return blink::WebString();
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;
396 void DispatchEventOnMainThread(const ProgressEvent &event);
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) {
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;
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();
415 p.resource_url = std::string(resource_url);
417 ppapi::PpapiGlobals::Get()->GetMainThreadMessageLoop()->PostTask(
419 base::Bind(&DispatchEventOnMainThread, p));
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)
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).
435 blink::WebFrame* frame = container->element().document().frame();
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());
445 v8::Context::Scope context_scope(context);
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,
456 container->element().dispatchEvent(blink_event);
458 blink::WebDOMProgressEvent blink_event(EventTypeToString(event.event_type),
459 event.length_is_computable,
462 container->element().dispatchEvent(blink_event);
466 void SetReadOnlyProperty(PP_Instance instance,
468 struct PP_Var value) {
469 content::PepperPluginInstance* plugin_instance =
470 content::PepperPluginInstance::Get(instance);
471 plugin_instance->SetEmbedProperty(key, value);
474 const PPB_NaCl_Private nacl_interface = {
478 &Are3DInterfacesDisabled,
479 &BrokerDuplicateHandle,
481 &CreateTemporaryFile,
482 &GetNumberOfProcessors,
484 &ReportTranslationFinished,
495 const PPB_NaCl_Private* GetNaClPrivateInterface() {
496 return &nacl_interface;
501 #endif // DISABLE_NACL