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"
11 #include "base/bind.h"
12 #include "base/bind_helpers.h"
13 #include "base/command_line.h"
14 #include "base/containers/scoped_ptr_hash_map.h"
16 #include "base/lazy_instance.h"
17 #include "base/logging.h"
18 #include "base/rand_util.h"
19 #include "components/nacl/common/nacl_host_messages.h"
20 #include "components/nacl/common/nacl_messages.h"
21 #include "components/nacl/common/nacl_switches.h"
22 #include "components/nacl/common/nacl_types.h"
23 #include "components/nacl/renderer/histogram.h"
24 #include "components/nacl/renderer/json_manifest.h"
25 #include "components/nacl/renderer/manifest_downloader.h"
26 #include "components/nacl/renderer/manifest_service_channel.h"
27 #include "components/nacl/renderer/nexe_load_manager.h"
28 #include "components/nacl/renderer/pnacl_translation_resource_host.h"
29 #include "components/nacl/renderer/sandbox_arch.h"
30 #include "components/nacl/renderer/trusted_plugin_channel.h"
31 #include "content/public/common/content_client.h"
32 #include "content/public/common/content_switches.h"
33 #include "content/public/common/sandbox_init.h"
34 #include "content/public/renderer/pepper_plugin_instance.h"
35 #include "content/public/renderer/render_thread.h"
36 #include "content/public/renderer/render_view.h"
37 #include "content/public/renderer/renderer_ppapi_host.h"
38 #include "net/base/data_url.h"
39 #include "net/base/net_errors.h"
40 #include "net/http/http_util.h"
41 #include "ppapi/c/pp_bool.h"
42 #include "ppapi/c/private/pp_file_handle.h"
43 #include "ppapi/native_client/src/trusted/plugin/nacl_entry_points.h"
44 #include "ppapi/shared_impl/ppapi_globals.h"
45 #include "ppapi/shared_impl/ppapi_permissions.h"
46 #include "ppapi/shared_impl/ppapi_preferences.h"
47 #include "ppapi/shared_impl/var.h"
48 #include "ppapi/thunk/enter.h"
49 #include "third_party/WebKit/public/platform/WebURLLoader.h"
50 #include "third_party/WebKit/public/web/WebDocument.h"
51 #include "third_party/WebKit/public/web/WebElement.h"
52 #include "third_party/WebKit/public/web/WebLocalFrame.h"
53 #include "third_party/WebKit/public/web/WebPluginContainer.h"
54 #include "third_party/WebKit/public/web/WebSecurityOrigin.h"
55 #include "third_party/WebKit/public/web/WebURLLoaderOptions.h"
56 #include "third_party/jsoncpp/source/include/json/reader.h"
57 #include "third_party/jsoncpp/source/include/json/value.h"
62 base::LazyInstance<scoped_refptr<PnaclTranslationResourceHost> >
63 g_pnacl_resource_host = LAZY_INSTANCE_INITIALIZER;
65 bool InitializePnaclResourceHost() {
66 // Must run on the main thread.
67 content::RenderThread* render_thread = content::RenderThread::Get();
70 if (!g_pnacl_resource_host.Get()) {
71 g_pnacl_resource_host.Get() = new PnaclTranslationResourceHost(
72 render_thread->GetIOMessageLoopProxy());
73 render_thread->AddFilter(g_pnacl_resource_host.Get());
79 InstanceInfo() : plugin_pid(base::kNullProcessId), plugin_child_id(0) {}
81 ppapi::PpapiPermissions permissions;
82 base::ProcessId plugin_pid;
84 IPC::ChannelHandle channel_handle;
87 typedef std::map<PP_Instance, InstanceInfo> InstanceInfoMap;
89 base::LazyInstance<InstanceInfoMap> g_instance_info =
90 LAZY_INSTANCE_INITIALIZER;
92 typedef base::ScopedPtrHashMap<PP_Instance, NexeLoadManager>
95 base::LazyInstance<NexeLoadManagerMap> g_load_manager_map =
96 LAZY_INSTANCE_INITIALIZER;
98 typedef base::ScopedPtrHashMap<int32_t, nacl::JsonManifest> JsonManifestMap;
100 base::LazyInstance<JsonManifestMap> g_manifest_map =
101 LAZY_INSTANCE_INITIALIZER;
103 base::LazyInstance<int32_t> g_next_manifest_id =
104 LAZY_INSTANCE_INITIALIZER;
106 // We have to define a method here since we can't use a static initializer.
107 int32_t GetPNaClManifestId() {
108 return std::numeric_limits<int32_t>::max();
111 nacl::NexeLoadManager* GetNexeLoadManager(PP_Instance instance) {
112 NexeLoadManagerMap& map = g_load_manager_map.Get();
113 NexeLoadManagerMap::iterator iter = map.find(instance);
114 if (iter != map.end())
119 int GetRoutingID(PP_Instance instance) {
120 // Check that we are on the main renderer thread.
121 DCHECK(content::RenderThread::Get());
122 content::RendererPpapiHost *host =
123 content::RendererPpapiHost::GetForPPInstance(instance);
126 return host->GetRoutingIDForWidget(instance);
129 // Returns whether the channel_handle is valid or not.
130 bool IsValidChannelHandle(const IPC::ChannelHandle& channel_handle) {
131 if (channel_handle.name.empty()) {
135 #if defined(OS_POSIX)
136 if (channel_handle.socket.fd == -1) {
144 // Callback invoked when an IPC channel connection is established.
145 // As we will establish multiple IPC channels, this takes the number
146 // of expected invocations and a callback. When all channels are established,
147 // the given callback will be invoked on the main thread. Its argument will be
148 // PP_OK if all the connections are successfully established. Otherwise,
149 // the first error code will be passed, and remaining errors will be ignored.
150 // Note that PP_CompletionCallback is designed to be called exactly once.
151 class ChannelConnectedCallback {
153 ChannelConnectedCallback(int num_expect_calls,
154 PP_CompletionCallback callback)
155 : num_remaining_calls_(num_expect_calls),
160 ~ChannelConnectedCallback() {
163 void Run(int32_t result) {
164 if (result_ == PP_OK && result != PP_OK) {
165 // This is the first error, so remember it.
169 --num_remaining_calls_;
170 if (num_remaining_calls_ > 0) {
171 // There still are some pending or on-going tasks. Wait for the results.
175 ppapi::PpapiGlobals::Get()->GetMainThreadMessageLoop()->PostTask(
177 base::Bind(callback_.func, callback_.user_data, result_));
181 int num_remaining_calls_;
182 PP_CompletionCallback callback_;
185 DISALLOW_COPY_AND_ASSIGN(ChannelConnectedCallback);
188 // Thin adapter from PPP_ManifestService to ManifestServiceChannel::Delegate.
189 // Note that user_data is managed by the caller of LaunchSelLdr. Please see
190 // also PP_ManifestService's comment for more details about resource
192 class ManifestServiceProxy : public ManifestServiceChannel::Delegate {
194 ManifestServiceProxy(const PPP_ManifestService* manifest_service,
196 : manifest_service_(*manifest_service),
197 user_data_(user_data) {
200 virtual ~ManifestServiceProxy() {
204 virtual void StartupInitializationComplete() OVERRIDE {
209 manifest_service_.StartupInitializationComplete(user_data_))) {
214 virtual void OpenResource(
215 const std::string& key,
216 const ManifestServiceChannel::OpenResourceCallback& callback) OVERRIDE {
220 // The allocated callback will be freed in DidOpenResource, which is always
221 // called regardless whether OpenResource() succeeds or fails.
222 if (!PP_ToBool(manifest_service_.OpenResource(
226 new ManifestServiceChannel::OpenResourceCallback(callback)))) {
232 static void DidOpenResource(void* user_data, PP_FileHandle file_handle) {
233 scoped_ptr<ManifestServiceChannel::OpenResourceCallback> callback(
234 static_cast<ManifestServiceChannel::OpenResourceCallback*>(user_data));
235 callback->Run(file_handle);
242 bool result = PP_ToBool(manifest_service_.Quit(user_data_));
247 PPP_ManifestService manifest_service_;
249 DISALLOW_COPY_AND_ASSIGN(ManifestServiceProxy);
252 // Launch NaCl's sel_ldr process.
253 void LaunchSelLdr(PP_Instance instance,
254 const char* alleged_url,
257 PP_Bool uses_nonsfi_mode,
258 PP_Bool enable_ppapi_dev,
259 PP_Bool enable_dyncode_syscalls,
260 PP_Bool enable_exception_handling,
261 PP_Bool enable_crash_throttling,
262 const PPP_ManifestService* manifest_service_interface,
263 void* manifest_service_user_data,
265 struct PP_Var* error_message,
266 PP_CompletionCallback callback) {
267 CHECK(ppapi::PpapiGlobals::Get()->GetMainThreadMessageLoop()->
268 BelongsToCurrentThread());
270 // Create the manifest service proxy here, so on error case, it will be
271 // destructed (without passing it to ManifestServiceChannel), and QUIT
272 // will be called in its destructor so that the caller of this function
273 // can free manifest_service_user_data properly.
274 scoped_ptr<ManifestServiceChannel::Delegate> manifest_service_proxy(
275 new ManifestServiceProxy(manifest_service_interface,
276 manifest_service_user_data));
278 FileDescriptor result_socket;
279 IPC::Sender* sender = content::RenderThread::Get();
281 *error_message = PP_MakeUndefined();
283 // If the nexe uses ppapi APIs, we need a routing ID.
284 // To get the routing ID, we must be on the main thread.
285 // Some nexes do not use ppapi and launch from the background thread,
286 // so those nexes can skip finding a routing_id.
288 routing_id = GetRoutingID(instance);
290 ppapi::PpapiGlobals::Get()->GetMainThreadMessageLoop()->PostTask(
292 base::Bind(callback.func, callback.user_data,
293 static_cast<int32_t>(PP_ERROR_FAILED)));
298 InstanceInfo instance_info;
299 instance_info.url = GURL(alleged_url);
301 uint32_t perm_bits = ppapi::PERMISSION_NONE;
302 // Conditionally block 'Dev' interfaces. We do this for the NaCl process, so
303 // it's clearer to developers when they are using 'Dev' inappropriately. We
304 // must also check on the trusted side of the proxy.
305 if (enable_ppapi_dev)
306 perm_bits |= ppapi::PERMISSION_DEV;
307 instance_info.permissions =
308 ppapi::PpapiPermissions::GetForCommandLine(perm_bits);
309 std::string error_message_string;
310 NaClLaunchResult launch_result;
312 if (!sender->Send(new NaClHostMsg_LaunchNaCl(
313 NaClLaunchParams(instance_info.url.spec(),
317 PP_ToBool(uses_nonsfi_mode),
318 PP_ToBool(enable_dyncode_syscalls),
319 PP_ToBool(enable_exception_handling),
320 PP_ToBool(enable_crash_throttling)),
322 &error_message_string))) {
323 ppapi::PpapiGlobals::Get()->GetMainThreadMessageLoop()->PostTask(
325 base::Bind(callback.func, callback.user_data,
326 static_cast<int32_t>(PP_ERROR_FAILED)));
329 if (!error_message_string.empty()) {
330 *error_message = ppapi::StringVar::StringToPPVar(error_message_string);
331 ppapi::PpapiGlobals::Get()->GetMainThreadMessageLoop()->PostTask(
333 base::Bind(callback.func, callback.user_data,
334 static_cast<int32_t>(PP_ERROR_FAILED)));
337 result_socket = launch_result.imc_channel_handle;
338 instance_info.channel_handle = launch_result.ppapi_ipc_channel_handle;
339 instance_info.plugin_pid = launch_result.plugin_pid;
340 instance_info.plugin_child_id = launch_result.plugin_child_id;
342 // Don't save instance_info if channel handle is invalid.
343 if (IsValidChannelHandle(instance_info.channel_handle))
344 g_instance_info.Get()[instance] = instance_info;
346 *(static_cast<NaClHandle*>(imc_handle)) = ToNativeHandle(result_socket);
348 // Here after, we starts to establish connections for TrustedPluginChannel
349 // and ManifestServiceChannel in parallel. The invocation of the callback
350 // is delegated to their connection completion callback.
351 base::Callback<void(int32_t)> connected_callback = base::Bind(
352 &ChannelConnectedCallback::Run,
353 base::Owned(new ChannelConnectedCallback(
354 2, // For TrustedPluginChannel and ManifestServiceChannel.
357 NexeLoadManager* load_manager = GetNexeLoadManager(instance);
358 DCHECK(load_manager);
360 // Stash the trusted handle as well.
362 IsValidChannelHandle(launch_result.trusted_ipc_channel_handle)) {
363 scoped_ptr<TrustedPluginChannel> trusted_plugin_channel(
364 new TrustedPluginChannel(
365 launch_result.trusted_ipc_channel_handle,
367 content::RenderThread::Get()->GetShutdownEvent()));
368 load_manager->set_trusted_plugin_channel(trusted_plugin_channel.Pass());
370 connected_callback.Run(PP_ERROR_FAILED);
373 // Stash the manifest service handle as well.
374 // For security hardening, disable the IPCs for open_resource() when they
375 // aren't needed. PNaCl doesn't expose open_resource(), and the new
376 // open_resource() IPCs are currently only used for Non-SFI NaCl so far,
377 // not SFI NaCl. Note that enable_dyncode_syscalls is true if and only if
378 // the plugin is a non-PNaCl plugin.
380 enable_dyncode_syscalls &&
382 IsValidChannelHandle(
383 launch_result.manifest_service_ipc_channel_handle)) {
384 scoped_ptr<ManifestServiceChannel> manifest_service_channel(
385 new ManifestServiceChannel(
386 launch_result.manifest_service_ipc_channel_handle,
388 manifest_service_proxy.Pass(),
389 content::RenderThread::Get()->GetShutdownEvent()));
390 load_manager->set_manifest_service_channel(
391 manifest_service_channel.Pass());
393 // Currently, manifest service works only on linux/non-SFI mode.
394 // On other platforms, the socket will not be created, and thus this
395 // condition needs to be handled as success.
396 connected_callback.Run(PP_OK);
400 // Forward declaration.
401 void ReportLoadError(PP_Instance instance,
403 const char* error_message,
404 const char* console_message);
406 PP_Bool StartPpapiProxy(PP_Instance instance) {
407 InstanceInfoMap& map = g_instance_info.Get();
408 InstanceInfoMap::iterator it = map.find(instance);
409 if (it == map.end()) {
410 DLOG(ERROR) << "Could not find instance ID";
413 InstanceInfo instance_info = it->second;
416 content::PepperPluginInstance* plugin_instance =
417 content::PepperPluginInstance::Get(instance);
418 if (!plugin_instance) {
419 DLOG(ERROR) << "GetInstance() failed";
423 PP_ExternalPluginResult result = plugin_instance->SwitchToOutOfProcessProxy(
424 base::FilePath().AppendASCII(instance_info.url.spec()),
425 instance_info.permissions,
426 instance_info.channel_handle,
427 instance_info.plugin_pid,
428 instance_info.plugin_child_id);
430 if (result == PP_EXTERNAL_PLUGIN_OK) {
431 // Log the amound of time that has passed between the trusted plugin being
432 // initialized and the untrusted plugin being initialized. This is
433 // (roughly) the cost of using NaCl, in terms of startup time.
434 NexeLoadManager* load_manager = GetNexeLoadManager(instance);
436 load_manager->ReportStartupOverhead();
438 } else if (result == PP_EXTERNAL_PLUGIN_ERROR_MODULE) {
439 ReportLoadError(instance,
440 PP_NACL_ERROR_START_PROXY_MODULE,
441 "could not initialize module.",
442 "could not initialize module.");
443 } else if (result == PP_EXTERNAL_PLUGIN_ERROR_INSTANCE) {
444 ReportLoadError(instance,
445 PP_NACL_ERROR_START_PROXY_MODULE,
446 "could not create instance.",
447 "could not create instance.");
452 int UrandomFD(void) {
453 #if defined(OS_POSIX)
454 return base::GetUrandomFD();
460 PP_Bool Are3DInterfacesDisabled() {
461 return PP_FromBool(CommandLine::ForCurrentProcess()->HasSwitch(
462 switches::kDisable3DAPIs));
465 int32_t BrokerDuplicateHandle(PP_FileHandle source_handle,
467 PP_FileHandle* target_handle,
468 uint32_t desired_access,
471 return content::BrokerDuplicateHandle(source_handle, process_id,
472 target_handle, desired_access,
479 PP_FileHandle GetReadonlyPnaclFD(const char* filename) {
480 IPC::PlatformFileForTransit out_fd = IPC::InvalidPlatformFileForTransit();
481 IPC::Sender* sender = content::RenderThread::Get();
483 if (!sender->Send(new NaClHostMsg_GetReadonlyPnaclFD(
484 std::string(filename),
486 return base::kInvalidPlatformFileValue;
488 if (out_fd == IPC::InvalidPlatformFileForTransit()) {
489 return base::kInvalidPlatformFileValue;
491 base::PlatformFile handle =
492 IPC::PlatformFileForTransitToPlatformFile(out_fd);
496 PP_FileHandle CreateTemporaryFile(PP_Instance instance) {
497 IPC::PlatformFileForTransit transit_fd = IPC::InvalidPlatformFileForTransit();
498 IPC::Sender* sender = content::RenderThread::Get();
500 if (!sender->Send(new NaClHostMsg_NaClCreateTemporaryFile(
502 return base::kInvalidPlatformFileValue;
505 if (transit_fd == IPC::InvalidPlatformFileForTransit()) {
506 return base::kInvalidPlatformFileValue;
509 base::PlatformFile handle = IPC::PlatformFileForTransitToPlatformFile(
514 int32_t GetNumberOfProcessors() {
515 int32_t num_processors;
516 IPC::Sender* sender = content::RenderThread::Get();
518 if(!sender->Send(new NaClHostMsg_NaClGetNumProcessors(&num_processors))) {
521 return num_processors;
524 PP_Bool IsNonSFIModeEnabled() {
525 #if defined(OS_LINUX)
526 return PP_FromBool(CommandLine::ForCurrentProcess()->HasSwitch(
527 switches::kEnableNaClNonSfiMode));
533 int32_t GetNexeFd(PP_Instance instance,
534 const char* pexe_url,
535 uint32_t abi_version,
537 const char* http_headers_param,
538 const char* extra_flags,
540 PP_FileHandle* handle,
541 struct PP_CompletionCallback callback) {
542 ppapi::thunk::EnterInstance enter(instance, callback);
544 return enter.retval();
545 if (!pexe_url || !is_hit || !handle)
546 return enter.SetResult(PP_ERROR_BADARGUMENT);
547 if (!InitializePnaclResourceHost())
548 return enter.SetResult(PP_ERROR_FAILED);
550 std::string http_headers(http_headers_param);
551 net::HttpUtil::HeadersIterator iter(
552 http_headers.begin(), http_headers.end(), "\r\n");
554 std::string last_modified;
556 bool has_no_store_header = false;
557 while (iter.GetNext()) {
558 if (StringToLowerASCII(iter.name()) == "last-modified")
559 last_modified = iter.values();
560 if (StringToLowerASCII(iter.name()) == "etag")
561 etag = iter.values();
562 if (StringToLowerASCII(iter.name()) == "cache-control") {
563 net::HttpUtil::ValuesIterator values_iter(
564 iter.values_begin(), iter.values_end(), ',');
565 while (values_iter.GetNext()) {
566 if (StringToLowerASCII(values_iter.value()) == "no-store")
567 has_no_store_header = true;
572 base::Time last_modified_time;
573 // If FromString fails, it doesn't touch last_modified_time and we just send
574 // the default-constructed null value.
575 base::Time::FromString(last_modified.c_str(), &last_modified_time);
577 PnaclCacheInfo cache_info;
578 cache_info.pexe_url = GURL(pexe_url);
579 cache_info.abi_version = abi_version;
580 cache_info.opt_level = opt_level;
581 cache_info.last_modified = last_modified_time;
582 cache_info.etag = etag;
583 cache_info.has_no_store_header = has_no_store_header;
584 cache_info.sandbox_isa = GetSandboxArch();
585 cache_info.extra_flags = std::string(extra_flags);
587 g_pnacl_resource_host.Get()->RequestNexeFd(
588 GetRoutingID(instance),
595 return enter.SetResult(PP_OK_COMPLETIONPENDING);
598 void ReportTranslationFinished(PP_Instance instance, PP_Bool success) {
599 // If the resource host isn't initialized, don't try to do that here.
600 // Just return because something is already very wrong.
601 if (g_pnacl_resource_host.Get() == NULL)
603 g_pnacl_resource_host.Get()->ReportTranslationFinished(instance, success);
606 PP_FileHandle OpenNaClExecutable(PP_Instance instance,
607 const char* file_url,
609 uint64_t* nonce_hi) {
610 // Fast path only works for installed file URLs.
612 if (!gurl.SchemeIs("chrome-extension"))
613 return PP_kInvalidFileHandle;
615 content::PepperPluginInstance* plugin_instance =
616 content::PepperPluginInstance::Get(instance);
617 // IMPORTANT: Make sure the document can request the given URL. If we don't
618 // check, a malicious app could probe the extension system. This enforces a
619 // same-origin policy which prevents the app from requesting resources from
621 blink::WebSecurityOrigin security_origin =
622 plugin_instance->GetContainer()->element().document().securityOrigin();
623 if (!security_origin.canRequest(gurl))
624 return PP_kInvalidFileHandle;
626 IPC::PlatformFileForTransit out_fd = IPC::InvalidPlatformFileForTransit();
627 IPC::Sender* sender = content::RenderThread::Get();
631 base::FilePath file_path;
633 new NaClHostMsg_OpenNaClExecutable(GetRoutingID(instance),
638 return base::kInvalidPlatformFileValue;
641 if (out_fd == IPC::InvalidPlatformFileForTransit()) {
642 return base::kInvalidPlatformFileValue;
645 base::PlatformFile handle =
646 IPC::PlatformFileForTransitToPlatformFile(out_fd);
650 void DispatchEventOnMainThread(PP_Instance instance,
651 PP_NaClEventType event_type,
652 const std::string& resource_url,
653 PP_Bool length_is_computable,
654 uint64_t loaded_bytes,
655 uint64_t total_bytes);
657 void DispatchEvent(PP_Instance instance,
658 PP_NaClEventType event_type,
659 const char *resource_url,
660 PP_Bool length_is_computable,
661 uint64_t loaded_bytes,
662 uint64_t total_bytes) {
663 ppapi::PpapiGlobals::Get()->GetMainThreadMessageLoop()->PostTask(
665 base::Bind(&DispatchEventOnMainThread,
668 std::string(resource_url),
669 length_is_computable,
674 void DispatchEventOnMainThread(PP_Instance instance,
675 PP_NaClEventType event_type,
676 const std::string& resource_url,
677 PP_Bool length_is_computable,
678 uint64_t loaded_bytes,
679 uint64_t total_bytes) {
680 NexeLoadManager* load_manager =
681 GetNexeLoadManager(instance);
682 // The instance may have been destroyed after we were scheduled, so do
683 // nothing if it's gone.
685 NexeLoadManager::ProgressEvent event(event_type);
686 event.resource_url = resource_url;
687 event.length_is_computable = PP_ToBool(length_is_computable);
688 event.loaded_bytes = loaded_bytes;
689 event.total_bytes = total_bytes;
690 load_manager->DispatchEvent(event);
694 void NexeFileDidOpen(PP_Instance instance,
698 int64_t nexe_bytes_read,
700 int64_t time_since_open) {
701 NexeLoadManager* load_manager = GetNexeLoadManager(instance);
703 load_manager->NexeFileDidOpen(pp_error,
712 void ReportLoadSuccess(PP_Instance instance,
714 uint64_t loaded_bytes,
715 uint64_t total_bytes) {
716 NexeLoadManager* load_manager = GetNexeLoadManager(instance);
718 load_manager->ReportLoadSuccess(url, loaded_bytes, total_bytes);
721 void ReportLoadError(PP_Instance instance,
723 const char* error_message,
724 const char* console_message) {
725 NexeLoadManager* load_manager = GetNexeLoadManager(instance);
727 load_manager->ReportLoadError(error, error_message, console_message);
730 void ReportLoadAbort(PP_Instance instance) {
731 NexeLoadManager* load_manager = GetNexeLoadManager(instance);
733 load_manager->ReportLoadAbort();
736 void NexeDidCrash(PP_Instance instance, const char* crash_log) {
737 NexeLoadManager* load_manager = GetNexeLoadManager(instance);
739 load_manager->NexeDidCrash(crash_log);
742 void InstanceCreated(PP_Instance instance) {
743 scoped_ptr<NexeLoadManager> new_load_manager(new NexeLoadManager(instance));
744 NexeLoadManagerMap& map = g_load_manager_map.Get();
745 DLOG_IF(ERROR, map.count(instance) != 0) << "Instance count should be 0";
746 map.add(instance, new_load_manager.Pass());
749 void InstanceDestroyed(PP_Instance instance) {
750 NexeLoadManagerMap& map = g_load_manager_map.Get();
751 DLOG_IF(ERROR, map.count(instance) == 0) << "Could not find instance ID";
752 // The erase may call NexeLoadManager's destructor prior to removing it from
753 // the map. In that case, it is possible for the trusted Plugin to re-enter
754 // the NexeLoadManager (e.g., by calling ReportLoadError). Passing out the
755 // NexeLoadManager to a local scoped_ptr just ensures that its entry is gone
756 // from the map prior to the destructor being invoked.
757 scoped_ptr<NexeLoadManager> temp(map.take(instance));
761 PP_Bool NaClDebugEnabledForURL(const char* alleged_nmf_url) {
762 if (!CommandLine::ForCurrentProcess()->HasSwitch(switches::kEnableNaClDebug))
765 IPC::Sender* sender = content::RenderThread::Get();
767 if(!sender->Send(new NaClHostMsg_NaClDebugEnabledForURL(
768 GURL(alleged_nmf_url),
772 return PP_FromBool(should_debug);
775 void LogToConsole(PP_Instance instance, const char* message) {
776 NexeLoadManager* load_manager = GetNexeLoadManager(instance);
777 DCHECK(load_manager);
779 load_manager->LogToConsole(std::string(message));
782 PP_NaClReadyState GetNaClReadyState(PP_Instance instance) {
783 NexeLoadManager* load_manager = GetNexeLoadManager(instance);
784 DCHECK(load_manager);
786 return load_manager->nacl_ready_state();
787 return PP_NACL_READY_STATE_UNSENT;
790 PP_Bool GetIsInstalled(PP_Instance instance) {
791 NexeLoadManager* load_manager = GetNexeLoadManager(instance);
792 DCHECK(load_manager);
794 return PP_FromBool(load_manager->is_installed());
798 int32_t GetExitStatus(PP_Instance instance) {
799 NexeLoadManager* load_manager = GetNexeLoadManager(instance);
800 DCHECK(load_manager);
802 return load_manager->exit_status();
806 void SetExitStatus(PP_Instance instance, int32_t exit_status) {
807 NexeLoadManager* load_manager = GetNexeLoadManager(instance);
808 DCHECK(load_manager);
810 return load_manager->set_exit_status(exit_status);
813 void Vlog(const char* message) {
817 void InitializePlugin(PP_Instance instance,
820 const char* argv[]) {
821 NexeLoadManager* load_manager = GetNexeLoadManager(instance);
822 DCHECK(load_manager);
824 load_manager->InitializePlugin(argc, argn, argv);
827 int64_t GetNexeSize(PP_Instance instance) {
828 NexeLoadManager* load_manager = GetNexeLoadManager(instance);
829 DCHECK(load_manager);
831 return load_manager->nexe_size();
835 PP_Bool RequestNaClManifest(PP_Instance instance,
837 PP_Bool* pp_is_data_uri) {
838 NexeLoadManager* load_manager = GetNexeLoadManager(instance);
839 DCHECK(load_manager);
842 bool result = load_manager->RequestNaClManifest(url, &is_data_uri);
843 *pp_is_data_uri = PP_FromBool(is_data_uri);
844 return PP_FromBool(result);
849 PP_Var GetManifestBaseURL(PP_Instance instance) {
850 NexeLoadManager* load_manager = GetNexeLoadManager(instance);
851 DCHECK(load_manager);
853 return PP_MakeUndefined();
854 const GURL& gurl = load_manager->manifest_base_url();
855 if (!gurl.is_valid())
856 return PP_MakeUndefined();
857 return ppapi::StringVar::StringToPPVar(gurl.spec());
860 PP_Bool ResolvesRelativeToPluginBaseURL(PP_Instance instance,
862 NexeLoadManager* load_manager = GetNexeLoadManager(instance);
863 DCHECK(load_manager);
866 const GURL& gurl = load_manager->plugin_base_url().Resolve(url);
867 if (!gurl.is_valid())
872 PP_Var ParseDataURL(const char* data_url) {
874 std::string mime_type;
877 if (!net::DataURL::Parse(gurl, &mime_type, &charset, &data))
878 return PP_MakeUndefined();
879 return ppapi::StringVar::StringToPPVar(data);
882 void ProcessNaClManifest(PP_Instance instance, const char* program_url) {
883 nacl::NexeLoadManager* load_manager = GetNexeLoadManager(instance);
885 load_manager->ProcessNaClManifest(program_url);
888 PP_Var GetManifestURLArgument(PP_Instance instance) {
889 nacl::NexeLoadManager* load_manager = GetNexeLoadManager(instance);
891 return ppapi::StringVar::StringToPPVar(
892 load_manager->GetManifestURLArgument());
894 return PP_MakeUndefined();
897 PP_Bool IsPNaCl(PP_Instance instance) {
898 nacl::NexeLoadManager* load_manager = GetNexeLoadManager(instance);
900 return PP_FromBool(load_manager->IsPNaCl());
904 PP_Bool DevInterfacesEnabled(PP_Instance instance) {
905 nacl::NexeLoadManager* load_manager = GetNexeLoadManager(instance);
907 return PP_FromBool(load_manager->DevInterfacesEnabled());
911 void DownloadManifestToBufferCompletion(PP_Instance instance,
912 struct PP_CompletionCallback callback,
913 struct PP_Var* out_data,
914 base::Time start_time,
915 PP_NaClError pp_nacl_error,
916 const std::string& data);
918 void DownloadManifestToBuffer(PP_Instance instance,
919 struct PP_Var* out_data,
920 struct PP_CompletionCallback callback) {
921 nacl::NexeLoadManager* load_manager = GetNexeLoadManager(instance);
922 DCHECK(load_manager);
924 ppapi::PpapiGlobals::Get()->GetMainThreadMessageLoop()->PostTask(
926 base::Bind(callback.func, callback.user_data,
927 static_cast<int32_t>(PP_ERROR_FAILED)));
930 const GURL& gurl = load_manager->manifest_base_url();
932 content::PepperPluginInstance* plugin_instance =
933 content::PepperPluginInstance::Get(instance);
934 blink::WebURLLoaderOptions options;
935 options.untrustedHTTP = true;
937 blink::WebSecurityOrigin security_origin =
938 plugin_instance->GetContainer()->element().document().securityOrigin();
939 // Options settings here follow the original behavior in the trusted
940 // plugin and PepperURLLoaderHost.
941 if (security_origin.canRequest(gurl)) {
942 options.allowCredentials = true;
945 options.crossOriginRequestPolicy =
946 blink::WebURLLoaderOptions::CrossOriginRequestPolicyUseAccessControl;
949 blink::WebFrame* frame =
950 plugin_instance->GetContainer()->element().document().frame();
951 blink::WebURLLoader* url_loader = frame->createAssociatedURLLoader(options);
952 blink::WebURLRequest request;
953 request.initialize();
954 request.setURL(gurl);
955 request.setFirstPartyForCookies(frame->document().firstPartyForCookies());
957 // ManifestDownloader deletes itself after invoking the callback.
958 ManifestDownloader* client = new ManifestDownloader(
959 load_manager->is_installed(),
960 base::Bind(DownloadManifestToBufferCompletion,
961 instance, callback, out_data, base::Time::Now()));
962 url_loader->loadAsynchronously(request, client);
965 void DownloadManifestToBufferCompletion(PP_Instance instance,
966 struct PP_CompletionCallback callback,
967 struct PP_Var* out_data,
968 base::Time start_time,
969 PP_NaClError pp_nacl_error,
970 const std::string& data) {
971 base::TimeDelta download_time = base::Time::Now() - start_time;
972 HistogramTimeSmall("NaCl.Perf.StartupTime.ManifestDownload",
973 download_time.InMilliseconds());
975 nacl::NexeLoadManager* load_manager = GetNexeLoadManager(instance);
977 callback.func(callback.user_data, PP_ERROR_ABORTED);
982 switch (pp_nacl_error) {
983 case PP_NACL_ERROR_LOAD_SUCCESS:
986 case PP_NACL_ERROR_MANIFEST_LOAD_URL:
987 pp_error = PP_ERROR_FAILED;
988 load_manager->ReportLoadError(PP_NACL_ERROR_MANIFEST_LOAD_URL,
989 "could not load manifest url.");
991 case PP_NACL_ERROR_MANIFEST_TOO_LARGE:
992 pp_error = PP_ERROR_FILETOOBIG;
993 load_manager->ReportLoadError(PP_NACL_ERROR_MANIFEST_TOO_LARGE,
994 "manifest file too large.");
996 case PP_NACL_ERROR_MANIFEST_NOACCESS_URL:
997 pp_error = PP_ERROR_NOACCESS;
998 load_manager->ReportLoadError(PP_NACL_ERROR_MANIFEST_NOACCESS_URL,
999 "access to manifest url was denied.");
1003 pp_error = PP_ERROR_FAILED;
1004 load_manager->ReportLoadError(PP_NACL_ERROR_MANIFEST_LOAD_URL,
1005 "could not load manifest url.");
1008 if (pp_error == PP_OK) {
1009 std::string contents;
1010 *out_data = ppapi::StringVar::StringToPPVar(data);
1012 callback.func(callback.user_data, pp_error);
1015 int32_t CreatePNaClManifest(PP_Instance /* instance */) {
1016 return GetPNaClManifestId();
1019 int32_t CreateJsonManifest(PP_Instance instance,
1020 const char* manifest_url,
1021 const char* isa_type,
1022 const char* manifest_data) {
1023 int32_t manifest_id = g_next_manifest_id.Get();
1024 g_next_manifest_id.Get()++;
1026 scoped_ptr<nacl::JsonManifest> j(
1027 new nacl::JsonManifest(
1030 PP_ToBool(IsNonSFIModeEnabled()),
1031 PP_ToBool(NaClDebugEnabledForURL(manifest_url))));
1032 JsonManifest::ErrorInfo error_info;
1033 if (j->Init(manifest_data, &error_info)) {
1034 g_manifest_map.Get().add(manifest_id, j.Pass());
1037 nacl::NexeLoadManager* load_manager = GetNexeLoadManager(instance);
1039 load_manager->ReportLoadError(error_info.error, error_info.string);
1043 void DestroyManifest(PP_Instance /* instance */,
1044 int32_t manifest_id) {
1045 if (manifest_id == GetPNaClManifestId())
1047 g_manifest_map.Get().erase(manifest_id);
1050 PP_Bool ManifestGetProgramURL(PP_Instance instance,
1051 int32_t manifest_id,
1052 PP_Var* pp_full_url,
1053 PP_PNaClOptions* pnacl_options,
1054 PP_Bool* pp_uses_nonsfi_mode) {
1055 nacl::NexeLoadManager* load_manager = GetNexeLoadManager(instance);
1056 if (manifest_id == GetPNaClManifestId()) {
1058 load_manager->ReportLoadError(
1059 PP_NACL_ERROR_MANIFEST_GET_NEXE_URL,
1060 "pnacl manifest does not contain a program.");
1065 JsonManifestMap::iterator it = g_manifest_map.Get().find(manifest_id);
1066 if (it == g_manifest_map.Get().end())
1069 bool uses_nonsfi_mode;
1070 std::string full_url;
1071 JsonManifest::ErrorInfo error_info;
1072 if (it->second->GetProgramURL(&full_url, pnacl_options, &uses_nonsfi_mode,
1074 *pp_full_url = ppapi::StringVar::StringToPPVar(full_url);
1075 *pp_uses_nonsfi_mode = PP_FromBool(uses_nonsfi_mode);
1080 load_manager->ReportLoadError(error_info.error, error_info.string);
1084 PP_Bool ManifestResolveKey(PP_Instance instance,
1085 int32_t manifest_id,
1087 PP_Var* pp_full_url,
1088 PP_PNaClOptions* pnacl_options) {
1089 if (manifest_id == GetPNaClManifestId()) {
1090 pnacl_options->translate = PP_FALSE;
1091 // We can only resolve keys in the files/ namespace.
1092 const std::string kFilesPrefix = "files/";
1093 std::string key_string(key);
1094 if (key_string.find(kFilesPrefix) == std::string::npos) {
1095 nacl::NexeLoadManager* load_manager = GetNexeLoadManager(instance);
1097 load_manager->ReportLoadError(PP_NACL_ERROR_MANIFEST_RESOLVE_URL,
1098 "key did not start with files/");
1101 std::string key_basename = key_string.substr(kFilesPrefix.length());
1102 std::string pnacl_url =
1103 std::string("chrome://pnacl-translator/") + GetSandboxArch() + "/" +
1105 *pp_full_url = ppapi::StringVar::StringToPPVar(pnacl_url);
1109 JsonManifestMap::iterator it = g_manifest_map.Get().find(manifest_id);
1110 if (it == g_manifest_map.Get().end())
1113 std::string full_url;
1114 bool ok = it->second->ResolveKey(key, &full_url, pnacl_options);
1116 *pp_full_url = ppapi::StringVar::StringToPPVar(full_url);
1117 return PP_FromBool(ok);
1120 PP_Bool GetPNaClResourceInfo(PP_Instance instance,
1121 const char* filename,
1122 PP_Var* llc_tool_name,
1123 PP_Var* ld_tool_name) {
1124 NexeLoadManager* load_manager = GetNexeLoadManager(instance);
1125 DCHECK(load_manager);
1129 base::PlatformFile file = GetReadonlyPnaclFD(filename);
1130 if (file == base::kInvalidPlatformFileValue) {
1131 load_manager->ReportLoadError(
1132 PP_NACL_ERROR_PNACL_RESOURCE_FETCH,
1133 "The Portable Native Client (pnacl) component is not "
1134 "installed. Please consult chrome://components for more "
1139 const int kBufferSize = 1 << 20;
1140 scoped_ptr<char[]> buffer(new char[kBufferSize]);
1141 if (base::ReadPlatformFile(file, 0, buffer.get(), kBufferSize) < 0) {
1142 load_manager->ReportLoadError(
1143 PP_NACL_ERROR_PNACL_RESOURCE_FETCH,
1144 std::string("PnaclResources::ReadResourceInfo reading failed for: ") +
1149 // Expect the JSON file to contain a top-level object (dictionary).
1150 Json::Reader json_reader;
1151 Json::Value json_data;
1152 if (!json_reader.parse(buffer.get(), json_data)) {
1153 load_manager->ReportLoadError(
1154 PP_NACL_ERROR_PNACL_RESOURCE_FETCH,
1155 std::string("Parsing resource info failed: JSON parse error: ") +
1156 json_reader.getFormattedErrorMessages());
1160 if (!json_data.isObject()) {
1161 load_manager->ReportLoadError(
1162 PP_NACL_ERROR_PNACL_RESOURCE_FETCH,
1163 "Parsing resource info failed: Malformed JSON dictionary");
1167 if (json_data.isMember("pnacl-llc-name")) {
1168 Json::Value json_name = json_data["pnacl-llc-name"];
1169 if (json_name.isString()) {
1170 std::string llc_tool_name_str = json_name.asString();
1171 *llc_tool_name = ppapi::StringVar::StringToPPVar(llc_tool_name_str);
1175 if (json_data.isMember("pnacl-ld-name")) {
1176 Json::Value json_name = json_data["pnacl-ld-name"];
1177 if (json_name.isString()) {
1178 std::string ld_tool_name_str = json_name.asString();
1179 *ld_tool_name = ppapi::StringVar::StringToPPVar(ld_tool_name_str);
1185 // Helper to std::accumulate that creates a comma-separated list from the input.
1186 std::string CommaAccumulator(const std::string &lhs, const std::string &rhs) {
1189 return lhs + "," + rhs;
1192 PP_Var GetCpuFeatureAttrs() {
1193 // PNaCl's translator from pexe to nexe can be told exactly what
1194 // capabilities the user's machine has because the pexe to nexe
1195 // translation is specific to the machine, and CPU information goes
1196 // into the translation cache. This allows the translator to generate
1199 // Care must be taken to avoid instructions which aren't supported by
1200 // the NaCl sandbox. Ideally the translator would do this, but there's
1201 // no point in not doing the whitelist here.
1203 // TODO(jfb) Some features are missing, either because the NaCl
1204 // sandbox doesn't support them, because base::CPU doesn't
1205 // detect them, or because they don't help vector shuffles
1206 // (and we omit them because it simplifies testing). Add the
1209 // TODO(jfb) The following is x86-specific. The base::CPU class
1210 // doesn't handle other architectures very well, and we
1211 // should at least detect the presence of ARM's integer
1213 std::vector<std::string> attrs;
1216 // On x86, SSE features are ordered: the most recent one implies the
1217 // others. Care is taken here to only specify the latest SSE version,
1218 // whereas non-SSE features don't follow this model: POPCNT is
1219 // effectively always implied by SSE4.2 but has to be specified
1222 // TODO: AVX2, AVX, SSE 4.2.
1223 if (cpu.has_sse41()) attrs.push_back("+sse4.1");
1224 // TODO: SSE 4A, SSE 4.
1225 else if (cpu.has_ssse3()) attrs.push_back("+ssse3");
1227 else if (cpu.has_sse2()) attrs.push_back("+sse2");
1229 // TODO: AES, POPCNT, LZCNT, ...
1231 return ppapi::StringVar::StringToPPVar(std::accumulate(
1232 attrs.begin(), attrs.end(), std::string(), CommaAccumulator));
1235 const PPB_NaCl_Private nacl_interface = {
1239 &Are3DInterfacesDisabled,
1240 &BrokerDuplicateHandle,
1241 &GetReadonlyPnaclFD,
1242 &CreateTemporaryFile,
1243 &GetNumberOfProcessors,
1244 &IsNonSFIModeEnabled,
1246 &ReportTranslationFinished,
1247 &OpenNaClExecutable,
1256 &NaClDebugEnabledForURL,
1266 &RequestNaClManifest,
1267 &GetManifestBaseURL,
1268 &ResolvesRelativeToPluginBaseURL,
1270 &ProcessNaClManifest,
1271 &GetManifestURLArgument,
1273 &DevInterfacesEnabled,
1274 &DownloadManifestToBuffer,
1275 &CreatePNaClManifest,
1276 &CreateJsonManifest,
1278 &ManifestGetProgramURL,
1279 &ManifestResolveKey,
1280 &GetPNaClResourceInfo,
1286 const PPB_NaCl_Private* GetNaClPrivateInterface() {
1287 return &nacl_interface;