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/files/file.h"
17 #include "base/lazy_instance.h"
18 #include "base/logging.h"
19 #include "base/rand_util.h"
20 #include "base/strings/string_split.h"
21 #include "base/strings/string_util.h"
22 #include "components/nacl/common/nacl_host_messages.h"
23 #include "components/nacl/common/nacl_messages.h"
24 #include "components/nacl/common/nacl_nonsfi_util.h"
25 #include "components/nacl/common/nacl_switches.h"
26 #include "components/nacl/common/nacl_types.h"
27 #include "components/nacl/renderer/file_downloader.h"
28 #include "components/nacl/renderer/histogram.h"
29 #include "components/nacl/renderer/json_manifest.h"
30 #include "components/nacl/renderer/manifest_downloader.h"
31 #include "components/nacl/renderer/manifest_service_channel.h"
32 #include "components/nacl/renderer/nexe_load_manager.h"
33 #include "components/nacl/renderer/platform_info.h"
34 #include "components/nacl/renderer/pnacl_translation_resource_host.h"
35 #include "components/nacl/renderer/progress_event.h"
36 #include "components/nacl/renderer/trusted_plugin_channel.h"
37 #include "content/public/common/content_client.h"
38 #include "content/public/common/content_switches.h"
39 #include "content/public/common/sandbox_init.h"
40 #include "content/public/renderer/pepper_plugin_instance.h"
41 #include "content/public/renderer/render_thread.h"
42 #include "content/public/renderer/render_view.h"
43 #include "content/public/renderer/renderer_ppapi_host.h"
44 #include "native_client/src/public/imc_types.h"
45 #include "net/base/data_url.h"
46 #include "net/base/net_errors.h"
47 #include "net/http/http_util.h"
48 #include "ppapi/c/pp_bool.h"
49 #include "ppapi/c/private/pp_file_handle.h"
50 #include "ppapi/shared_impl/ppapi_globals.h"
51 #include "ppapi/shared_impl/ppapi_permissions.h"
52 #include "ppapi/shared_impl/ppapi_preferences.h"
53 #include "ppapi/shared_impl/var.h"
54 #include "ppapi/shared_impl/var_tracker.h"
55 #include "ppapi/thunk/enter.h"
56 #include "third_party/WebKit/public/platform/WebURLLoader.h"
57 #include "third_party/WebKit/public/platform/WebURLResponse.h"
58 #include "third_party/WebKit/public/web/WebDocument.h"
59 #include "third_party/WebKit/public/web/WebElement.h"
60 #include "third_party/WebKit/public/web/WebLocalFrame.h"
61 #include "third_party/WebKit/public/web/WebPluginContainer.h"
62 #include "third_party/WebKit/public/web/WebSecurityOrigin.h"
63 #include "third_party/WebKit/public/web/WebURLLoaderOptions.h"
64 #include "third_party/jsoncpp/source/include/json/reader.h"
65 #include "third_party/jsoncpp/source/include/json/value.h"
70 // The pseudo-architecture used to indicate portable native client.
71 const char* const kPortableArch
= "portable";
73 // The base URL for resources used by the PNaCl translator processes.
74 const char* kPNaClTranslatorBaseUrl
= "chrome://pnacl-translator/";
76 base::LazyInstance
<scoped_refptr
<PnaclTranslationResourceHost
> >
77 g_pnacl_resource_host
= LAZY_INSTANCE_INITIALIZER
;
79 bool InitializePnaclResourceHost() {
80 // Must run on the main thread.
81 content::RenderThread
* render_thread
= content::RenderThread::Get();
84 if (!g_pnacl_resource_host
.Get()) {
85 g_pnacl_resource_host
.Get() = new PnaclTranslationResourceHost(
86 render_thread
->GetIOMessageLoopProxy());
87 render_thread
->AddFilter(g_pnacl_resource_host
.Get());
93 InstanceInfo() : plugin_pid(base::kNullProcessId
), plugin_child_id(0) {}
95 ppapi::PpapiPermissions permissions
;
96 base::ProcessId plugin_pid
;
98 IPC::ChannelHandle channel_handle
;
101 typedef std::map
<PP_Instance
, InstanceInfo
> InstanceInfoMap
;
103 base::LazyInstance
<InstanceInfoMap
> g_instance_info
=
104 LAZY_INSTANCE_INITIALIZER
;
106 typedef base::ScopedPtrHashMap
<PP_Instance
, NexeLoadManager
>
109 base::LazyInstance
<NexeLoadManagerMap
> g_load_manager_map
=
110 LAZY_INSTANCE_INITIALIZER
;
112 nacl::NexeLoadManager
* GetNexeLoadManager(PP_Instance instance
) {
113 NexeLoadManagerMap
& map
= g_load_manager_map
.Get();
114 NexeLoadManagerMap::iterator iter
= map
.find(instance
);
115 if (iter
!= map
.end())
120 static const PP_NaClFileInfo kInvalidNaClFileInfo
= {
121 PP_kInvalidFileHandle
,
126 int GetRoutingID(PP_Instance instance
) {
127 // Check that we are on the main renderer thread.
128 DCHECK(content::RenderThread::Get());
129 content::RendererPpapiHost
*host
=
130 content::RendererPpapiHost::GetForPPInstance(instance
);
133 return host
->GetRoutingIDForWidget(instance
);
136 // Returns whether the channel_handle is valid or not.
137 bool IsValidChannelHandle(const IPC::ChannelHandle
& channel_handle
) {
138 if (channel_handle
.name
.empty()) {
142 #if defined(OS_POSIX)
143 if (channel_handle
.socket
.fd
== -1) {
151 void PostPPCompletionCallback(PP_CompletionCallback callback
,
153 ppapi::PpapiGlobals::Get()->GetMainThreadMessageLoop()->PostTask(
155 base::Bind(callback
.func
, callback
.user_data
, status
));
158 bool ManifestResolveKey(PP_Instance instance
,
159 bool is_helper_process
,
160 const std::string
& key
,
161 std::string
* full_url
,
162 PP_PNaClOptions
* pnacl_options
);
164 typedef base::Callback
<void(int32_t, const PP_NaClFileInfo
&)>
165 DownloadFileCallback
;
167 void DownloadFile(PP_Instance instance
,
168 const std::string
& url
,
169 const DownloadFileCallback
& callback
);
171 PP_Bool
StartPpapiProxy(PP_Instance instance
);
173 // Thin adapter from PPP_ManifestService to ManifestServiceChannel::Delegate.
174 // Note that user_data is managed by the caller of LaunchSelLdr. Please see
175 // also PP_ManifestService's comment for more details about resource
177 class ManifestServiceProxy
: public ManifestServiceChannel::Delegate
{
179 ManifestServiceProxy(PP_Instance pp_instance
)
180 : pp_instance_(pp_instance
) {
183 virtual ~ManifestServiceProxy() { }
185 virtual void StartupInitializationComplete() OVERRIDE
{
186 if (StartPpapiProxy(pp_instance_
) == PP_TRUE
) {
187 JsonManifest
* manifest
= GetJsonManifest(pp_instance_
);
188 NexeLoadManager
* load_manager
= GetNexeLoadManager(pp_instance_
);
189 if (load_manager
&& manifest
) {
190 std::string full_url
;
191 PP_PNaClOptions pnacl_options
;
192 bool uses_nonsfi_mode
;
193 JsonManifest::ErrorInfo error_info
;
194 if (manifest
->GetProgramURL(&full_url
,
198 int64_t nexe_size
= load_manager
->nexe_size();
199 load_manager
->ReportLoadSuccess(full_url
, nexe_size
, nexe_size
);
205 virtual void OpenResource(
206 const std::string
& key
,
207 const ManifestServiceChannel::OpenResourceCallback
& callback
) OVERRIDE
{
208 DCHECK(ppapi::PpapiGlobals::Get()->GetMainThreadMessageLoop()->
209 BelongsToCurrentThread());
212 // TODO(teravest): Clean up pnacl_options logic in JsonManifest so we don't
213 // have to initialize it like this here.
214 PP_PNaClOptions pnacl_options
;
215 pnacl_options
.translate
= PP_FALSE
;
216 pnacl_options
.is_debug
= PP_FALSE
;
217 pnacl_options
.opt_level
= 2;
218 if (!ManifestResolveKey(pp_instance_
, false, key
, &url
, &pnacl_options
)) {
219 base::MessageLoop::current()->PostTask(
221 base::Bind(callback
, base::Passed(base::File())));
225 // We have to call DidDownloadFile, even if this object is destroyed, so
226 // that the handle inside PP_NaClFileInfo isn't leaked. This means that the
227 // callback passed to this function shouldn't have a weak pointer to an
230 // TODO(teravest): Make a type like PP_NaClFileInfo to use for DownloadFile
231 // that would close the file handle on destruction.
232 DownloadFile(pp_instance_
, url
,
233 base::Bind(&ManifestServiceProxy::DidDownloadFile
, callback
));
237 static void DidDownloadFile(
238 ManifestServiceChannel::OpenResourceCallback callback
,
240 const PP_NaClFileInfo
& file_info
) {
241 if (pp_error
!= PP_OK
) {
242 callback
.Run(base::File());
245 callback
.Run(base::File(file_info
.handle
));
248 PP_Instance pp_instance_
;
249 DISALLOW_COPY_AND_ASSIGN(ManifestServiceProxy
);
252 blink::WebURLLoader
* CreateWebURLLoader(const blink::WebDocument
& document
,
254 blink::WebURLLoaderOptions options
;
255 options
.untrustedHTTP
= true;
257 // Options settings here follow the original behavior in the trusted
258 // plugin and PepperURLLoaderHost.
259 if (document
.securityOrigin().canRequest(gurl
)) {
260 options
.allowCredentials
= true;
263 options
.crossOriginRequestPolicy
=
264 blink::WebURLLoaderOptions::CrossOriginRequestPolicyUseAccessControl
;
266 return document
.frame()->createAssociatedURLLoader(options
);
269 blink::WebURLRequest
CreateWebURLRequest(const blink::WebDocument
& document
,
271 blink::WebURLRequest request
;
272 request
.initialize();
273 request
.setURL(gurl
);
274 request
.setFirstPartyForCookies(document
.firstPartyForCookies());
278 int32_t FileDownloaderToPepperError(FileDownloader::Status status
) {
280 case FileDownloader::SUCCESS
:
282 case FileDownloader::ACCESS_DENIED
:
283 return PP_ERROR_NOACCESS
;
284 case FileDownloader::FAILED
:
285 return PP_ERROR_FAILED
;
286 // No default case, to catch unhandled Status values.
288 return PP_ERROR_FAILED
;
291 // Launch NaCl's sel_ldr process.
292 void LaunchSelLdr(PP_Instance instance
,
293 PP_Bool main_service_runtime
,
294 const char* alleged_url
,
295 const PP_NaClFileInfo
* nexe_file_info
,
298 PP_Bool uses_nonsfi_mode
,
299 PP_Bool enable_ppapi_dev
,
300 PP_Bool enable_dyncode_syscalls
,
301 PP_Bool enable_exception_handling
,
302 PP_Bool enable_crash_throttling
,
304 PP_CompletionCallback callback
) {
305 CHECK(ppapi::PpapiGlobals::Get()->GetMainThreadMessageLoop()->
306 BelongsToCurrentThread());
308 // Create the manifest service proxy here, so on error case, it will be
309 // destructed (without passing it to ManifestServiceChannel).
310 scoped_ptr
<ManifestServiceChannel::Delegate
> manifest_service_proxy(
311 new ManifestServiceProxy(instance
));
313 FileDescriptor result_socket
;
314 IPC::Sender
* sender
= content::RenderThread::Get();
317 // If the nexe uses ppapi APIs, we need a routing ID.
318 // To get the routing ID, we must be on the main thread.
319 // Some nexes do not use ppapi and launch from the background thread,
320 // so those nexes can skip finding a routing_id.
322 routing_id
= GetRoutingID(instance
);
324 if (nexe_file_info
->handle
!= PP_kInvalidFileHandle
) {
325 base::File
closer(nexe_file_info
->handle
);
327 ppapi::PpapiGlobals::Get()->GetMainThreadMessageLoop()->PostTask(
329 base::Bind(callback
.func
, callback
.user_data
,
330 static_cast<int32_t>(PP_ERROR_FAILED
)));
335 InstanceInfo instance_info
;
336 instance_info
.url
= GURL(alleged_url
);
338 uint32_t perm_bits
= ppapi::PERMISSION_NONE
;
339 // Conditionally block 'Dev' interfaces. We do this for the NaCl process, so
340 // it's clearer to developers when they are using 'Dev' inappropriately. We
341 // must also check on the trusted side of the proxy.
342 if (enable_ppapi_dev
)
343 perm_bits
|= ppapi::PERMISSION_DEV
;
344 instance_info
.permissions
=
345 ppapi::PpapiPermissions::GetForCommandLine(perm_bits
);
346 std::string error_message_string
;
347 NaClLaunchResult launch_result
;
349 content::RendererPpapiHost
* host
=
350 content::RendererPpapiHost::GetForPPInstance(instance
);
351 if (!sender
->Send(new NaClHostMsg_LaunchNaCl(
353 instance_info
.url
.spec(),
354 host
->ShareHandleWithRemote(nexe_file_info
->handle
, true),
358 PP_ToBool(uses_nonsfi_mode
),
359 PP_ToBool(enable_dyncode_syscalls
),
360 PP_ToBool(enable_exception_handling
),
361 PP_ToBool(enable_crash_throttling
)),
363 &error_message_string
))) {
364 ppapi::PpapiGlobals::Get()->GetMainThreadMessageLoop()->PostTask(
366 base::Bind(callback
.func
, callback
.user_data
,
367 static_cast<int32_t>(PP_ERROR_FAILED
)));
371 if (!error_message_string
.empty()) {
372 if (PP_ToBool(main_service_runtime
)) {
373 NexeLoadManager
* load_manager
= GetNexeLoadManager(instance
);
375 load_manager
->ReportLoadError(PP_NACL_ERROR_SEL_LDR_LAUNCH
,
376 "ServiceRuntime: failed to start",
377 error_message_string
);
380 ppapi::PpapiGlobals::Get()->GetMainThreadMessageLoop()->PostTask(
382 base::Bind(callback
.func
, callback
.user_data
,
383 static_cast<int32_t>(PP_ERROR_FAILED
)));
386 result_socket
= launch_result
.imc_channel_handle
;
387 instance_info
.channel_handle
= launch_result
.ppapi_ipc_channel_handle
;
388 instance_info
.plugin_pid
= launch_result
.plugin_pid
;
389 instance_info
.plugin_child_id
= launch_result
.plugin_child_id
;
391 // Don't save instance_info if channel handle is invalid.
392 if (IsValidChannelHandle(instance_info
.channel_handle
))
393 g_instance_info
.Get()[instance
] = instance_info
;
395 *(static_cast<NaClHandle
*>(imc_handle
)) = ToNativeHandle(result_socket
);
397 NexeLoadManager
* load_manager
= GetNexeLoadManager(instance
);
398 DCHECK(load_manager
);
400 PostPPCompletionCallback(callback
, PP_ERROR_FAILED
);
404 // Create the trusted plugin channel.
405 if (IsValidChannelHandle(launch_result
.trusted_ipc_channel_handle
)) {
406 scoped_ptr
<TrustedPluginChannel
> trusted_plugin_channel(
407 new TrustedPluginChannel(
408 launch_result
.trusted_ipc_channel_handle
));
409 load_manager
->set_trusted_plugin_channel(trusted_plugin_channel
.Pass());
411 PostPPCompletionCallback(callback
, PP_ERROR_FAILED
);
415 // Create the manifest service handle as well.
416 // For security hardening, disable the IPCs for open_resource() when they
417 // aren't needed. PNaCl doesn't expose open_resource(), and the new
418 // open_resource() IPCs are currently only used for Non-SFI NaCl so far,
419 // not SFI NaCl. Note that enable_dyncode_syscalls is true if and only if
420 // the plugin is a non-PNaCl plugin.
422 enable_dyncode_syscalls
&&
424 IsValidChannelHandle(
425 launch_result
.manifest_service_ipc_channel_handle
)) {
426 scoped_ptr
<ManifestServiceChannel
> manifest_service_channel(
427 new ManifestServiceChannel(
428 launch_result
.manifest_service_ipc_channel_handle
,
429 base::Bind(&PostPPCompletionCallback
, callback
),
430 manifest_service_proxy
.Pass(),
431 content::RenderThread::Get()->GetShutdownEvent()));
432 load_manager
->set_manifest_service_channel(
433 manifest_service_channel
.Pass());
435 // Currently, manifest service works only on linux/non-SFI mode.
436 // On other platforms, the socket will not be created, and thus this
437 // condition needs to be handled as success.
438 PostPPCompletionCallback(callback
, PP_OK
);
442 PP_Bool
StartPpapiProxy(PP_Instance instance
) {
443 NexeLoadManager
* load_manager
= GetNexeLoadManager(instance
);
444 DCHECK(load_manager
);
448 content::PepperPluginInstance
* plugin_instance
=
449 content::PepperPluginInstance::Get(instance
);
450 if (!plugin_instance
) {
451 DLOG(ERROR
) << "GetInstance() failed";
455 InstanceInfoMap
& map
= g_instance_info
.Get();
456 InstanceInfoMap::iterator it
= map
.find(instance
);
457 if (it
== map
.end()) {
458 DLOG(ERROR
) << "Could not find instance ID";
461 InstanceInfo instance_info
= it
->second
;
464 PP_ExternalPluginResult result
= plugin_instance
->SwitchToOutOfProcessProxy(
465 base::FilePath().AppendASCII(instance_info
.url
.spec()),
466 instance_info
.permissions
,
467 instance_info
.channel_handle
,
468 instance_info
.plugin_pid
,
469 instance_info
.plugin_child_id
);
471 if (result
== PP_EXTERNAL_PLUGIN_OK
) {
472 // Log the amound of time that has passed between the trusted plugin being
473 // initialized and the untrusted plugin being initialized. This is
474 // (roughly) the cost of using NaCl, in terms of startup time.
475 load_manager
->ReportStartupOverhead();
477 } else if (result
== PP_EXTERNAL_PLUGIN_ERROR_MODULE
) {
478 load_manager
->ReportLoadError(PP_NACL_ERROR_START_PROXY_MODULE
,
479 "could not initialize module.");
480 } else if (result
== PP_EXTERNAL_PLUGIN_ERROR_INSTANCE
) {
481 load_manager
->ReportLoadError(PP_NACL_ERROR_START_PROXY_MODULE
,
482 "could not create instance.");
487 int UrandomFD(void) {
488 #if defined(OS_POSIX)
489 return base::GetUrandomFD();
495 PP_Bool
Are3DInterfacesDisabled() {
496 return PP_FromBool(CommandLine::ForCurrentProcess()->HasSwitch(
497 switches::kDisable3DAPIs
));
500 int32_t BrokerDuplicateHandle(PP_FileHandle source_handle
,
502 PP_FileHandle
* target_handle
,
503 uint32_t desired_access
,
506 return content::BrokerDuplicateHandle(source_handle
, process_id
,
507 target_handle
, desired_access
,
514 // Convert a URL to a filename for GetReadonlyPnaclFd.
515 // Must be kept in sync with PnaclCanOpenFile() in
516 // components/nacl/browser/nacl_file_host.cc.
517 std::string
PnaclComponentURLToFilename(const std::string
& url
) {
518 // PNaCl component URLs aren't arbitrary URLs; they are always either
519 // generated from ManifestResolveKey or PnaclResources::ReadResourceInfo.
520 // So, it's safe to just use string parsing operations here instead of
522 DCHECK(StartsWithASCII(url
, kPNaClTranslatorBaseUrl
, true));
523 std::string r
= url
.substr(std::string(kPNaClTranslatorBaseUrl
).length());
525 // Use white-listed-chars.
527 static const char* white_list
= "abcdefghijklmnopqrstuvwxyz0123456789_";
528 replace_pos
= r
.find_first_not_of(white_list
);
529 while(replace_pos
!= std::string::npos
) {
530 r
= r
.replace(replace_pos
, 1, "_");
531 replace_pos
= r
.find_first_not_of(white_list
);
536 PP_FileHandle
GetReadonlyPnaclFd(const char* url
,
539 uint64_t* nonce_hi
) {
540 std::string filename
= PnaclComponentURLToFilename(url
);
541 IPC::PlatformFileForTransit out_fd
= IPC::InvalidPlatformFileForTransit();
542 IPC::Sender
* sender
= content::RenderThread::Get();
544 if (!sender
->Send(new NaClHostMsg_GetReadonlyPnaclFD(
545 std::string(filename
), is_executable
,
546 &out_fd
, nonce_lo
, nonce_hi
))) {
547 return PP_kInvalidFileHandle
;
549 if (out_fd
== IPC::InvalidPlatformFileForTransit()) {
550 return PP_kInvalidFileHandle
;
552 return IPC::PlatformFileForTransitToPlatformFile(out_fd
);
555 void GetReadExecPnaclFd(const char* url
,
556 PP_NaClFileInfo
* out_file_info
) {
557 *out_file_info
= kInvalidNaClFileInfo
;
558 out_file_info
->handle
= GetReadonlyPnaclFd(url
, true /* is_executable */,
559 &out_file_info
->token_lo
,
560 &out_file_info
->token_hi
);
563 PP_FileHandle
CreateTemporaryFile(PP_Instance instance
) {
564 IPC::PlatformFileForTransit transit_fd
= IPC::InvalidPlatformFileForTransit();
565 IPC::Sender
* sender
= content::RenderThread::Get();
567 if (!sender
->Send(new NaClHostMsg_NaClCreateTemporaryFile(
569 return PP_kInvalidFileHandle
;
572 if (transit_fd
== IPC::InvalidPlatformFileForTransit()) {
573 return PP_kInvalidFileHandle
;
576 return IPC::PlatformFileForTransitToPlatformFile(transit_fd
);
579 int32_t GetNumberOfProcessors() {
580 int32_t num_processors
;
581 IPC::Sender
* sender
= content::RenderThread::Get();
583 if(!sender
->Send(new NaClHostMsg_NaClGetNumProcessors(&num_processors
))) {
586 return num_processors
;
589 PP_Bool
PPIsNonSFIModeEnabled() {
590 return PP_FromBool(IsNonSFIModeEnabled());
593 void GetNexeFd(PP_Instance instance
,
594 const std::string
& pexe_url
,
596 const base::Time
& last_modified_time
,
597 const std::string
& etag
,
598 bool has_no_store_header
,
599 base::Callback
<void(int32_t, bool, PP_FileHandle
)> callback
) {
600 if (!InitializePnaclResourceHost()) {
601 ppapi::PpapiGlobals::Get()->GetMainThreadMessageLoop()->PostTask(
604 static_cast<int32_t>(PP_ERROR_FAILED
),
606 PP_kInvalidFileHandle
));
610 PnaclCacheInfo cache_info
;
611 cache_info
.pexe_url
= GURL(pexe_url
);
612 // TODO(dschuff): Get this value from the pnacl json file after it
613 // rolls in from NaCl.
614 cache_info
.abi_version
= 1;
615 cache_info
.opt_level
= opt_level
;
616 cache_info
.last_modified
= last_modified_time
;
617 cache_info
.etag
= etag
;
618 cache_info
.has_no_store_header
= has_no_store_header
;
619 cache_info
.sandbox_isa
= GetSandboxArch();
620 cache_info
.extra_flags
= GetCpuFeatures();
622 g_pnacl_resource_host
.Get()->RequestNexeFd(
623 GetRoutingID(instance
),
629 void ReportTranslationFinished(PP_Instance instance
,
633 int64_t compile_time_us
) {
634 if (success
== PP_TRUE
) {
635 static const int32_t kUnknownOptLevel
= 4;
636 if (opt_level
< 0 || opt_level
> 3)
637 opt_level
= kUnknownOptLevel
;
638 HistogramEnumerate("NaCl.Options.PNaCl.OptLevel",
640 kUnknownOptLevel
+ 1);
641 HistogramKBPerSec("NaCl.Perf.PNaClLoadTime.CompileKBPerSec",
644 HistogramSizeKB("NaCl.Perf.Size.Pexe", pexe_size
/ 1024);
646 NexeLoadManager
* load_manager
= GetNexeLoadManager(instance
);
648 base::TimeDelta total_time
= base::Time::Now() -
649 load_manager
->pnacl_start_time();
650 HistogramTimeTranslation("NaCl.Perf.PNaClLoadTime.TotalUncachedTime",
651 total_time
.InMilliseconds());
652 HistogramKBPerSec("NaCl.Perf.PNaClLoadTime.TotalUncachedKBPerSec",
654 total_time
.InMicroseconds());
658 // If the resource host isn't initialized, don't try to do that here.
659 // Just return because something is already very wrong.
660 if (g_pnacl_resource_host
.Get() == NULL
)
662 g_pnacl_resource_host
.Get()->ReportTranslationFinished(instance
, success
);
665 PP_FileHandle
OpenNaClExecutable(PP_Instance instance
,
666 const char* file_url
,
668 uint64_t* nonce_hi
) {
669 // Fast path only works for installed file URLs.
671 if (!gurl
.SchemeIs("chrome-extension"))
672 return PP_kInvalidFileHandle
;
674 content::PepperPluginInstance
* plugin_instance
=
675 content::PepperPluginInstance::Get(instance
);
676 if (!plugin_instance
)
677 return PP_kInvalidFileHandle
;
678 // IMPORTANT: Make sure the document can request the given URL. If we don't
679 // check, a malicious app could probe the extension system. This enforces a
680 // same-origin policy which prevents the app from requesting resources from
682 blink::WebSecurityOrigin security_origin
=
683 plugin_instance
->GetContainer()->element().document().securityOrigin();
684 if (!security_origin
.canRequest(gurl
))
685 return PP_kInvalidFileHandle
;
687 IPC::PlatformFileForTransit out_fd
= IPC::InvalidPlatformFileForTransit();
688 IPC::Sender
* sender
= content::RenderThread::Get();
692 base::FilePath file_path
;
694 new NaClHostMsg_OpenNaClExecutable(GetRoutingID(instance
),
699 return PP_kInvalidFileHandle
;
702 if (out_fd
== IPC::InvalidPlatformFileForTransit())
703 return PP_kInvalidFileHandle
;
705 return IPC::PlatformFileForTransitToPlatformFile(out_fd
);
708 void DispatchEvent(PP_Instance instance
,
709 PP_NaClEventType event_type
,
710 const char *resource_url
,
711 PP_Bool length_is_computable
,
712 uint64_t loaded_bytes
,
713 uint64_t total_bytes
) {
714 ProgressEvent
event(event_type
,
716 PP_ToBool(length_is_computable
),
719 DispatchProgressEvent(instance
, event
);
722 void ReportLoadSuccess(PP_Instance instance
,
723 uint64_t loaded_bytes
,
724 uint64_t total_bytes
) {
725 NexeLoadManager
* load_manager
= GetNexeLoadManager(instance
);
727 load_manager
->ReportLoadSuccess(load_manager
->program_url(),
733 void ReportLoadError(PP_Instance instance
,
735 const char* error_message
) {
736 NexeLoadManager
* load_manager
= GetNexeLoadManager(instance
);
738 load_manager
->ReportLoadError(error
, error_message
);
741 void ReportLoadAbort(PP_Instance instance
) {
742 NexeLoadManager
* load_manager
= GetNexeLoadManager(instance
);
744 load_manager
->ReportLoadAbort();
747 void NexeDidCrash(PP_Instance instance
, const char* crash_log
) {
748 NexeLoadManager
* load_manager
= GetNexeLoadManager(instance
);
750 load_manager
->NexeDidCrash(crash_log
);
753 void InstanceCreated(PP_Instance instance
) {
754 scoped_ptr
<NexeLoadManager
> new_load_manager(new NexeLoadManager(instance
));
755 NexeLoadManagerMap
& map
= g_load_manager_map
.Get();
756 DLOG_IF(ERROR
, map
.count(instance
) != 0) << "Instance count should be 0";
757 map
.add(instance
, new_load_manager
.Pass());
760 void InstanceDestroyed(PP_Instance instance
) {
761 DeleteJsonManifest(instance
);
763 NexeLoadManagerMap
& map
= g_load_manager_map
.Get();
764 DLOG_IF(ERROR
, map
.count(instance
) == 0) << "Could not find instance ID";
765 // The erase may call NexeLoadManager's destructor prior to removing it from
766 // the map. In that case, it is possible for the trusted Plugin to re-enter
767 // the NexeLoadManager (e.g., by calling ReportLoadError). Passing out the
768 // NexeLoadManager to a local scoped_ptr just ensures that its entry is gone
769 // from the map prior to the destructor being invoked.
770 scoped_ptr
<NexeLoadManager
> temp(map
.take(instance
));
774 PP_Bool
NaClDebugEnabledForURL(const char* alleged_nmf_url
) {
775 if (!CommandLine::ForCurrentProcess()->HasSwitch(switches::kEnableNaClDebug
))
778 IPC::Sender
* sender
= content::RenderThread::Get();
780 if(!sender
->Send(new NaClHostMsg_NaClDebugEnabledForURL(
781 GURL(alleged_nmf_url
),
785 return PP_FromBool(should_debug
);
788 void LogToConsole(PP_Instance instance
, const char* message
) {
789 NexeLoadManager
* load_manager
= GetNexeLoadManager(instance
);
790 DCHECK(load_manager
);
792 load_manager
->LogToConsole(std::string(message
));
795 PP_NaClReadyState
GetNaClReadyState(PP_Instance instance
) {
796 NexeLoadManager
* load_manager
= GetNexeLoadManager(instance
);
797 DCHECK(load_manager
);
799 return load_manager
->nacl_ready_state();
800 return PP_NACL_READY_STATE_UNSENT
;
803 int32_t GetExitStatus(PP_Instance instance
) {
804 NexeLoadManager
* load_manager
= GetNexeLoadManager(instance
);
805 DCHECK(load_manager
);
807 return load_manager
->exit_status();
811 void SetExitStatus(PP_Instance instance
, int32_t exit_status
) {
812 NexeLoadManager
* load_manager
= GetNexeLoadManager(instance
);
813 DCHECK(load_manager
);
815 return load_manager
->set_exit_status(exit_status
);
818 void Vlog(const char* message
) {
822 void InitializePlugin(PP_Instance instance
,
825 const char* argv
[]) {
826 NexeLoadManager
* load_manager
= GetNexeLoadManager(instance
);
827 DCHECK(load_manager
);
829 load_manager
->InitializePlugin(argc
, argn
, argv
);
832 int64_t GetNexeSize(PP_Instance instance
) {
833 NexeLoadManager
* load_manager
= GetNexeLoadManager(instance
);
834 DCHECK(load_manager
);
836 return load_manager
->nexe_size();
840 void DownloadManifestToBuffer(PP_Instance instance
,
841 struct PP_CompletionCallback callback
);
843 bool CreateJsonManifest(PP_Instance instance
,
844 const std::string
& manifest_url
,
845 const std::string
& manifest_data
);
847 void RequestNaClManifest(PP_Instance instance
,
848 PP_CompletionCallback callback
) {
849 NexeLoadManager
* load_manager
= GetNexeLoadManager(instance
);
850 DCHECK(load_manager
);
852 ppapi::PpapiGlobals::Get()->GetMainThreadMessageLoop()->PostTask(
854 base::Bind(callback
.func
, callback
.user_data
,
855 static_cast<int32_t>(PP_ERROR_FAILED
)));
859 std::string url
= load_manager
->GetManifestURLArgument();
860 if (url
.empty() || !load_manager
->RequestNaClManifest(url
)) {
861 ppapi::PpapiGlobals::Get()->GetMainThreadMessageLoop()->PostTask(
863 base::Bind(callback
.func
, callback
.user_data
,
864 static_cast<int32_t>(PP_ERROR_FAILED
)));
868 const GURL
& base_url
= load_manager
->manifest_base_url();
869 if (base_url
.SchemeIs("data")) {
871 std::string mime_type
;
874 int32_t error
= PP_ERROR_FAILED
;
875 if (net::DataURL::Parse(gurl
, &mime_type
, &charset
, &data
)) {
876 if (data
.size() <= ManifestDownloader::kNaClManifestMaxFileBytes
) {
877 if (CreateJsonManifest(instance
, base_url
.spec(), data
))
880 load_manager
->ReportLoadError(PP_NACL_ERROR_MANIFEST_TOO_LARGE
,
881 "manifest file too large.");
884 load_manager
->ReportLoadError(PP_NACL_ERROR_MANIFEST_LOAD_URL
,
885 "could not load manifest url.");
887 ppapi::PpapiGlobals::Get()->GetMainThreadMessageLoop()->PostTask(
889 base::Bind(callback
.func
, callback
.user_data
, error
));
891 DownloadManifestToBuffer(instance
, callback
);
895 PP_Var
GetManifestBaseURL(PP_Instance instance
) {
896 NexeLoadManager
* load_manager
= GetNexeLoadManager(instance
);
897 DCHECK(load_manager
);
899 return PP_MakeUndefined();
900 const GURL
& gurl
= load_manager
->manifest_base_url();
901 if (!gurl
.is_valid())
902 return PP_MakeUndefined();
903 return ppapi::StringVar::StringToPPVar(gurl
.spec());
906 void ProcessNaClManifest(PP_Instance instance
, const char* program_url
) {
907 nacl::NexeLoadManager
* load_manager
= GetNexeLoadManager(instance
);
909 load_manager
->ProcessNaClManifest(program_url
);
912 PP_Bool
DevInterfacesEnabled(PP_Instance instance
) {
913 nacl::NexeLoadManager
* load_manager
= GetNexeLoadManager(instance
);
915 return PP_FromBool(load_manager
->DevInterfacesEnabled());
919 void DownloadManifestToBufferCompletion(PP_Instance instance
,
920 struct PP_CompletionCallback callback
,
921 base::Time start_time
,
922 PP_NaClError pp_nacl_error
,
923 const std::string
& data
);
925 void DownloadManifestToBuffer(PP_Instance instance
,
926 struct PP_CompletionCallback callback
) {
927 nacl::NexeLoadManager
* load_manager
= GetNexeLoadManager(instance
);
928 DCHECK(load_manager
);
929 content::PepperPluginInstance
* plugin_instance
=
930 content::PepperPluginInstance::Get(instance
);
931 if (!load_manager
|| !plugin_instance
) {
932 ppapi::PpapiGlobals::Get()->GetMainThreadMessageLoop()->PostTask(
934 base::Bind(callback
.func
, callback
.user_data
,
935 static_cast<int32_t>(PP_ERROR_FAILED
)));
937 const blink::WebDocument
& document
=
938 plugin_instance
->GetContainer()->element().document();
940 const GURL
& gurl
= load_manager
->manifest_base_url();
941 scoped_ptr
<blink::WebURLLoader
> url_loader(
942 CreateWebURLLoader(document
, gurl
));
943 blink::WebURLRequest request
= CreateWebURLRequest(document
, gurl
);
945 // ManifestDownloader deletes itself after invoking the callback.
946 ManifestDownloader
* manifest_downloader
= new ManifestDownloader(
948 load_manager
->is_installed(),
949 base::Bind(DownloadManifestToBufferCompletion
,
950 instance
, callback
, base::Time::Now()));
951 manifest_downloader
->Load(request
);
954 void DownloadManifestToBufferCompletion(PP_Instance instance
,
955 struct PP_CompletionCallback callback
,
956 base::Time start_time
,
957 PP_NaClError pp_nacl_error
,
958 const std::string
& data
) {
959 base::TimeDelta download_time
= base::Time::Now() - start_time
;
960 HistogramTimeSmall("NaCl.Perf.StartupTime.ManifestDownload",
961 download_time
.InMilliseconds());
963 nacl::NexeLoadManager
* load_manager
= GetNexeLoadManager(instance
);
965 callback
.func(callback
.user_data
, PP_ERROR_ABORTED
);
970 switch (pp_nacl_error
) {
971 case PP_NACL_ERROR_LOAD_SUCCESS
:
974 case PP_NACL_ERROR_MANIFEST_LOAD_URL
:
975 pp_error
= PP_ERROR_FAILED
;
976 load_manager
->ReportLoadError(PP_NACL_ERROR_MANIFEST_LOAD_URL
,
977 "could not load manifest url.");
979 case PP_NACL_ERROR_MANIFEST_TOO_LARGE
:
980 pp_error
= PP_ERROR_FILETOOBIG
;
981 load_manager
->ReportLoadError(PP_NACL_ERROR_MANIFEST_TOO_LARGE
,
982 "manifest file too large.");
984 case PP_NACL_ERROR_MANIFEST_NOACCESS_URL
:
985 pp_error
= PP_ERROR_NOACCESS
;
986 load_manager
->ReportLoadError(PP_NACL_ERROR_MANIFEST_NOACCESS_URL
,
987 "access to manifest url was denied.");
991 pp_error
= PP_ERROR_FAILED
;
992 load_manager
->ReportLoadError(PP_NACL_ERROR_MANIFEST_LOAD_URL
,
993 "could not load manifest url.");
996 if (pp_error
== PP_OK
) {
997 std::string base_url
= load_manager
->manifest_base_url().spec();
998 if (!CreateJsonManifest(instance
, base_url
, data
))
999 pp_error
= PP_ERROR_FAILED
;
1001 callback
.func(callback
.user_data
, pp_error
);
1004 bool CreateJsonManifest(PP_Instance instance
,
1005 const std::string
& manifest_url
,
1006 const std::string
& manifest_data
) {
1007 HistogramSizeKB("NaCl.Perf.Size.Manifest",
1008 static_cast<int32_t>(manifest_data
.length() / 1024));
1010 nacl::NexeLoadManager
* load_manager
= GetNexeLoadManager(instance
);
1014 const char* isa_type
;
1015 if (load_manager
->IsPNaCl())
1016 isa_type
= kPortableArch
;
1018 isa_type
= GetSandboxArch();
1020 scoped_ptr
<nacl::JsonManifest
> j(
1021 new nacl::JsonManifest(
1022 manifest_url
.c_str(),
1024 IsNonSFIModeEnabled(),
1025 PP_ToBool(NaClDebugEnabledForURL(manifest_url
.c_str()))));
1026 JsonManifest::ErrorInfo error_info
;
1027 if (j
->Init(manifest_data
.c_str(), &error_info
)) {
1028 AddJsonManifest(instance
, j
.Pass());
1031 load_manager
->ReportLoadError(error_info
.error
, error_info
.string
);
1035 PP_Bool
ManifestGetProgramURL(PP_Instance instance
,
1036 PP_Var
* pp_full_url
,
1037 PP_PNaClOptions
* pnacl_options
,
1038 PP_Bool
* pp_uses_nonsfi_mode
) {
1039 nacl::NexeLoadManager
* load_manager
= GetNexeLoadManager(instance
);
1041 JsonManifest
* manifest
= GetJsonManifest(instance
);
1042 if (manifest
== NULL
)
1045 bool uses_nonsfi_mode
;
1046 std::string full_url
;
1047 JsonManifest::ErrorInfo error_info
;
1048 if (manifest
->GetProgramURL(&full_url
, pnacl_options
, &uses_nonsfi_mode
,
1050 *pp_full_url
= ppapi::StringVar::StringToPPVar(full_url
);
1051 *pp_uses_nonsfi_mode
= PP_FromBool(uses_nonsfi_mode
);
1056 load_manager
->ReportLoadError(error_info
.error
, error_info
.string
);
1060 bool ManifestResolveKey(PP_Instance instance
,
1061 bool is_helper_process
,
1062 const std::string
& key
,
1063 std::string
* full_url
,
1064 PP_PNaClOptions
* pnacl_options
) {
1065 // For "helper" processes (llc and ld, for PNaCl translation), we resolve
1066 // keys manually as there is no existing .nmf file to parse.
1067 if (is_helper_process
) {
1068 pnacl_options
->translate
= PP_FALSE
;
1069 // We can only resolve keys in the files/ namespace.
1070 const std::string kFilesPrefix
= "files/";
1071 if (key
.find(kFilesPrefix
) == std::string::npos
) {
1072 nacl::NexeLoadManager
* load_manager
= GetNexeLoadManager(instance
);
1074 load_manager
->ReportLoadError(PP_NACL_ERROR_MANIFEST_RESOLVE_URL
,
1075 "key did not start with files/");
1078 std::string key_basename
= key
.substr(kFilesPrefix
.length());
1079 *full_url
= std::string(kPNaClTranslatorBaseUrl
) + GetSandboxArch() + "/" +
1084 JsonManifest
* manifest
= GetJsonManifest(instance
);
1085 if (manifest
== NULL
)
1088 return manifest
->ResolveKey(key
, full_url
, pnacl_options
);
1091 PP_Bool
GetPNaClResourceInfo(PP_Instance instance
,
1092 PP_Var
* llc_tool_name
,
1093 PP_Var
* ld_tool_name
) {
1094 static const char kFilename
[] = "chrome://pnacl-translator/pnacl.json";
1095 NexeLoadManager
* load_manager
= GetNexeLoadManager(instance
);
1096 DCHECK(load_manager
);
1100 uint64_t nonce_lo
= 0;
1101 uint64_t nonce_hi
= 0;
1102 base::File
file(GetReadonlyPnaclFd(kFilename
, false /* is_executable */,
1103 &nonce_lo
, &nonce_hi
));
1104 if (!file
.IsValid()) {
1105 load_manager
->ReportLoadError(
1106 PP_NACL_ERROR_PNACL_RESOURCE_FETCH
,
1107 "The Portable Native Client (pnacl) component is not "
1108 "installed. Please consult chrome://components for more "
1113 base::File::Info file_info
;
1114 if (!file
.GetInfo(&file_info
)) {
1115 load_manager
->ReportLoadError(
1116 PP_NACL_ERROR_PNACL_RESOURCE_FETCH
,
1117 std::string("GetPNaClResourceInfo, GetFileInfo failed for: ") +
1122 if (file_info
.size
> 1 << 20) {
1123 load_manager
->ReportLoadError(
1124 PP_NACL_ERROR_PNACL_RESOURCE_FETCH
,
1125 std::string("GetPNaClResourceInfo, file too large: ") + kFilename
);
1129 scoped_ptr
<char[]> buffer(new char[file_info
.size
+ 1]);
1130 if (buffer
.get() == NULL
) {
1131 load_manager
->ReportLoadError(
1132 PP_NACL_ERROR_PNACL_RESOURCE_FETCH
,
1133 std::string("GetPNaClResourceInfo, couldn't allocate for: ") +
1138 int rc
= file
.Read(0, buffer
.get(), file_info
.size
);
1140 load_manager
->ReportLoadError(
1141 PP_NACL_ERROR_PNACL_RESOURCE_FETCH
,
1142 std::string("GetPNaClResourceInfo, reading failed for: ") + kFilename
);
1146 // Null-terminate the bytes we we read from the file.
1147 buffer
.get()[rc
] = 0;
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 PP_Var
GetCpuFeatureAttrs() {
1186 return ppapi::StringVar::StringToPPVar(GetCpuFeatures());
1189 void PostMessageToJavaScriptMainThread(PP_Instance instance
,
1190 const std::string
& message
) {
1191 content::PepperPluginInstance
* plugin_instance
=
1192 content::PepperPluginInstance::Get(instance
);
1193 if (plugin_instance
) {
1194 PP_Var message_var
= ppapi::StringVar::StringToPPVar(message
);
1195 plugin_instance
->PostMessageToJavaScript(message_var
);
1196 ppapi::PpapiGlobals::Get()->GetVarTracker()->ReleaseVar(message_var
);
1200 void PostMessageToJavaScript(PP_Instance instance
, const char* message
) {
1201 ppapi::PpapiGlobals::Get()->GetMainThreadMessageLoop()->PostTask(
1203 base::Bind(&PostMessageToJavaScriptMainThread
,
1205 std::string(message
)));
1208 // Encapsulates some of the state for a call to DownloadNexe to prevent
1209 // argument lists from getting too long.
1210 struct DownloadNexeRequest
{
1211 PP_Instance instance
;
1213 PP_CompletionCallback callback
;
1214 base::Time start_time
;
1217 // A utility class to ensure that we don't send progress events more often than
1218 // every 10ms for a given file.
1219 class ProgressEventRateLimiter
{
1221 explicit ProgressEventRateLimiter(PP_Instance instance
)
1222 : instance_(instance
) { }
1224 void ReportProgress(const std::string
& url
,
1225 int64_t total_bytes_received
,
1226 int64_t total_bytes_to_be_received
) {
1227 base::Time now
= base::Time::Now();
1228 if (now
- last_event_
> base::TimeDelta::FromMilliseconds(10)) {
1229 DispatchProgressEvent(instance_
,
1230 ProgressEvent(PP_NACL_EVENT_PROGRESS
,
1232 total_bytes_to_be_received
>= 0,
1233 total_bytes_received
,
1234 total_bytes_to_be_received
));
1240 PP_Instance instance_
;
1241 base::Time last_event_
;
1244 void DownloadNexeCompletion(const DownloadNexeRequest
& request
,
1245 PP_NaClFileInfo
* out_file_info
,
1246 FileDownloader::Status status
,
1247 base::File target_file
,
1250 void DownloadNexe(PP_Instance instance
,
1252 PP_NaClFileInfo
* out_file_info
,
1253 PP_CompletionCallback callback
) {
1255 CHECK(out_file_info
);
1256 DownloadNexeRequest request
;
1257 request
.instance
= instance
;
1259 request
.callback
= callback
;
1260 request
.start_time
= base::Time::Now();
1262 // Try the fast path for retrieving the file first.
1263 PP_FileHandle handle
= OpenNaClExecutable(instance
,
1265 &out_file_info
->token_lo
,
1266 &out_file_info
->token_hi
);
1267 if (handle
!= PP_kInvalidFileHandle
) {
1268 DownloadNexeCompletion(request
,
1270 FileDownloader::SUCCESS
,
1276 // The fast path didn't work, we'll fetch the file using URLLoader and write
1277 // it to local storage.
1278 base::File
target_file(CreateTemporaryFile(instance
));
1281 content::PepperPluginInstance
* plugin_instance
=
1282 content::PepperPluginInstance::Get(instance
);
1283 if (!plugin_instance
) {
1284 ppapi::PpapiGlobals::Get()->GetMainThreadMessageLoop()->PostTask(
1286 base::Bind(callback
.func
, callback
.user_data
,
1287 static_cast<int32_t>(PP_ERROR_FAILED
)));
1289 const blink::WebDocument
& document
=
1290 plugin_instance
->GetContainer()->element().document();
1291 scoped_ptr
<blink::WebURLLoader
> url_loader(
1292 CreateWebURLLoader(document
, gurl
));
1293 blink::WebURLRequest url_request
= CreateWebURLRequest(document
, gurl
);
1295 ProgressEventRateLimiter
* tracker
= new ProgressEventRateLimiter(instance
);
1297 // FileDownloader deletes itself after invoking DownloadNexeCompletion.
1298 FileDownloader
* file_downloader
= new FileDownloader(
1301 base::Bind(&DownloadNexeCompletion
, request
, out_file_info
),
1302 base::Bind(&ProgressEventRateLimiter::ReportProgress
,
1303 base::Owned(tracker
), std::string(url
)));
1304 file_downloader
->Load(url_request
);
1307 void DownloadNexeCompletion(const DownloadNexeRequest
& request
,
1308 PP_NaClFileInfo
* out_file_info
,
1309 FileDownloader::Status status
,
1310 base::File target_file
,
1312 int32_t pp_error
= FileDownloaderToPepperError(status
);
1313 int64_t bytes_read
= -1;
1314 if (pp_error
== PP_OK
&& target_file
.IsValid()) {
1315 base::File::Info info
;
1316 if (target_file
.GetInfo(&info
))
1317 bytes_read
= info
.size
;
1320 if (bytes_read
== -1) {
1321 target_file
.Close();
1322 pp_error
= PP_ERROR_FAILED
;
1325 base::TimeDelta download_time
= base::Time::Now() - request
.start_time
;
1327 NexeLoadManager
* load_manager
= GetNexeLoadManager(request
.instance
);
1329 load_manager
->NexeFileDidOpen(pp_error
,
1337 if (pp_error
== PP_OK
&& target_file
.IsValid())
1338 out_file_info
->handle
= target_file
.TakePlatformFile();
1340 out_file_info
->handle
= PP_kInvalidFileHandle
;
1342 request
.callback
.func(request
.callback
.user_data
, pp_error
);
1345 void DownloadFileCompletion(
1346 const DownloadFileCallback
& callback
,
1347 FileDownloader::Status status
,
1350 int32_t pp_error
= FileDownloaderToPepperError(status
);
1351 PP_NaClFileInfo file_info
;
1352 if (pp_error
== PP_OK
) {
1353 file_info
.handle
= file
.TakePlatformFile();
1354 file_info
.token_lo
= 0;
1355 file_info
.token_hi
= 0;
1357 file_info
= kInvalidNaClFileInfo
;
1360 callback
.Run(pp_error
, file_info
);
1363 void DownloadFile(PP_Instance instance
,
1364 const std::string
& url
,
1365 const DownloadFileCallback
& callback
) {
1366 DCHECK(ppapi::PpapiGlobals::Get()->GetMainThreadMessageLoop()->
1367 BelongsToCurrentThread());
1369 NexeLoadManager
* load_manager
= GetNexeLoadManager(instance
);
1370 DCHECK(load_manager
);
1371 if (!load_manager
) {
1372 base::MessageLoop::current()->PostTask(
1374 base::Bind(callback
,
1375 static_cast<int32_t>(PP_ERROR_FAILED
),
1376 kInvalidNaClFileInfo
));
1380 // Handle special PNaCl support files which are installed on the user's
1382 if (url
.find(kPNaClTranslatorBaseUrl
, 0) == 0) {
1383 PP_NaClFileInfo file_info
= kInvalidNaClFileInfo
;
1384 PP_FileHandle handle
= GetReadonlyPnaclFd(url
.c_str(),
1385 false /* is_executable */,
1386 &file_info
.token_lo
,
1387 &file_info
.token_hi
);
1388 if (handle
== PP_kInvalidFileHandle
) {
1389 base::MessageLoop::current()->PostTask(
1391 base::Bind(callback
,
1392 static_cast<int32_t>(PP_ERROR_FAILED
),
1393 kInvalidNaClFileInfo
));
1396 file_info
.handle
= handle
;
1397 base::MessageLoop::current()->PostTask(
1399 base::Bind(callback
, static_cast<int32_t>(PP_OK
), file_info
));
1403 // We have to ensure that this url resolves relative to the plugin base url
1404 // before downloading it.
1405 const GURL
& test_gurl
= load_manager
->plugin_base_url().Resolve(url
);
1406 if (!test_gurl
.is_valid()) {
1407 base::MessageLoop::current()->PostTask(
1409 base::Bind(callback
,
1410 static_cast<int32_t>(PP_ERROR_FAILED
),
1411 kInvalidNaClFileInfo
));
1415 // Try the fast path for retrieving the file first.
1416 uint64_t file_token_lo
= 0;
1417 uint64_t file_token_hi
= 0;
1418 PP_FileHandle file_handle
= OpenNaClExecutable(instance
,
1422 if (file_handle
!= PP_kInvalidFileHandle
) {
1423 PP_NaClFileInfo file_info
;
1424 file_info
.handle
= file_handle
;
1425 file_info
.token_lo
= file_token_lo
;
1426 file_info
.token_hi
= file_token_hi
;
1427 base::MessageLoop::current()->PostTask(
1429 base::Bind(callback
, static_cast<int32_t>(PP_OK
), file_info
));
1433 // The fast path didn't work, we'll fetch the file using URLLoader and write
1434 // it to local storage.
1435 base::File
target_file(CreateTemporaryFile(instance
));
1438 content::PepperPluginInstance
* plugin_instance
=
1439 content::PepperPluginInstance::Get(instance
);
1440 if (!plugin_instance
) {
1441 base::MessageLoop::current()->PostTask(
1443 base::Bind(callback
,
1444 static_cast<int32_t>(PP_ERROR_FAILED
),
1445 kInvalidNaClFileInfo
));
1447 const blink::WebDocument
& document
=
1448 plugin_instance
->GetContainer()->element().document();
1449 scoped_ptr
<blink::WebURLLoader
> url_loader(
1450 CreateWebURLLoader(document
, gurl
));
1451 blink::WebURLRequest url_request
= CreateWebURLRequest(document
, gurl
);
1453 ProgressEventRateLimiter
* tracker
= new ProgressEventRateLimiter(instance
);
1455 // FileDownloader deletes itself after invoking DownloadNexeCompletion.
1456 FileDownloader
* file_downloader
= new FileDownloader(
1459 base::Bind(&DownloadFileCompletion
, callback
),
1460 base::Bind(&ProgressEventRateLimiter::ReportProgress
,
1461 base::Owned(tracker
), std::string(url
)));
1462 file_downloader
->Load(url_request
);
1465 void ReportSelLdrStatus(PP_Instance instance
,
1466 int32_t load_status
,
1467 int32_t max_status
) {
1468 HistogramEnumerate("NaCl.LoadStatus.SelLdr", load_status
, max_status
);
1469 NexeLoadManager
* load_manager
= GetNexeLoadManager(instance
);
1470 DCHECK(load_manager
);
1474 // Gather data to see if being installed changes load outcomes.
1475 const char* name
= load_manager
->is_installed() ?
1476 "NaCl.LoadStatus.SelLdr.InstalledApp" :
1477 "NaCl.LoadStatus.SelLdr.NotInstalledApp";
1478 HistogramEnumerate(name
, load_status
, max_status
);
1481 void LogTranslateTime(const char* histogram_name
,
1482 int64_t time_in_us
) {
1483 ppapi::PpapiGlobals::Get()->GetMainThreadMessageLoop()->PostTask(
1485 base::Bind(&HistogramTimeTranslation
,
1486 std::string(histogram_name
),
1487 time_in_us
/ 1000));
1490 void DidOpenManifestEntry(PP_NaClFileInfo
* out_file_info
,
1491 PP_CompletionCallback callback
,
1493 const PP_NaClFileInfo
& file_info
) {
1494 if (pp_error
== PP_OK
)
1495 *out_file_info
= file_info
;
1496 callback
.func(callback
.user_data
, pp_error
);
1499 void OpenManifestEntry(PP_Instance instance
,
1500 PP_Bool is_helper_process
,
1502 PP_NaClFileInfo
* out_file_info
,
1503 PP_CompletionCallback callback
) {
1505 PP_PNaClOptions pnacl_options
;
1506 pnacl_options
.translate
= PP_FALSE
;
1507 pnacl_options
.is_debug
= PP_FALSE
;
1508 pnacl_options
.opt_level
= 2;
1509 if (!ManifestResolveKey(instance
,
1510 PP_ToBool(is_helper_process
),
1514 PostPPCompletionCallback(callback
, PP_ERROR_FAILED
);
1517 // TODO(teravest): Make a type like PP_NaClFileInfo to use for DownloadFile
1518 // that would close the file handle on destruction.
1519 DownloadFile(instance
, url
,
1520 base::Bind(&DidOpenManifestEntry
, out_file_info
, callback
));
1523 void SetPNaClStartTime(PP_Instance instance
) {
1524 NexeLoadManager
* load_manager
= GetNexeLoadManager(instance
);
1526 load_manager
->set_pnacl_start_time(base::Time::Now());
1529 class PexeDownloader
: public blink::WebURLLoaderClient
{
1531 PexeDownloader(PP_Instance instance
,
1532 scoped_ptr
<blink::WebURLLoader
> url_loader
,
1533 const std::string
& pexe_url
,
1534 int32_t pexe_opt_level
,
1535 const PPP_PexeStreamHandler
* stream_handler
,
1536 void* stream_handler_user_data
)
1537 : instance_(instance
),
1538 url_loader_(url_loader
.Pass()),
1539 pexe_url_(pexe_url
),
1540 pexe_opt_level_(pexe_opt_level
),
1541 stream_handler_(stream_handler
),
1542 stream_handler_user_data_(stream_handler_user_data
),
1544 expected_content_length_(-1),
1545 weak_factory_(this) { }
1547 void Load(const blink::WebURLRequest
& request
) {
1548 url_loader_
->loadAsynchronously(request
, this);
1552 virtual void didReceiveResponse(blink::WebURLLoader
* loader
,
1553 const blink::WebURLResponse
& response
) {
1554 success_
= (response
.httpStatusCode() == 200);
1558 expected_content_length_
= response
.expectedContentLength();
1560 // Defer loading after receiving headers. This is because we may already
1561 // have a cached translated nexe, so check for that now.
1562 url_loader_
->setDefersLoading(true);
1564 std::string etag
= response
.httpHeaderField("etag").utf8();
1565 std::string last_modified
=
1566 response
.httpHeaderField("last-modified").utf8();
1567 base::Time last_modified_time
;
1568 base::Time::FromString(last_modified
.c_str(), &last_modified_time
);
1570 bool has_no_store_header
= false;
1571 std::string cache_control
=
1572 response
.httpHeaderField("cache-control").utf8();
1574 std::vector
<std::string
> values
;
1575 base::SplitString(cache_control
, ',', &values
);
1576 for (std::vector
<std::string
>::const_iterator it
= values
.begin();
1579 if (StringToLowerASCII(*it
) == "no-store")
1580 has_no_store_header
= true;
1583 GetNexeFd(instance_
,
1588 has_no_store_header
,
1589 base::Bind(&PexeDownloader::didGetNexeFd
,
1590 weak_factory_
.GetWeakPtr()));
1593 virtual void didGetNexeFd(int32_t pp_error
,
1595 PP_FileHandle file_handle
) {
1597 stream_handler_
->DidCacheHit(stream_handler_user_data_
, file_handle
);
1599 // We delete the PexeDownloader at this point since we successfully got a
1600 // cached, translated nexe.
1604 stream_handler_
->DidCacheMiss(stream_handler_user_data_
,
1605 expected_content_length_
);
1607 // No translated nexe was found in the cache, so we should download the
1608 // file to start streaming it.
1609 url_loader_
->setDefersLoading(false);
1612 virtual void didReceiveData(blink::WebURLLoader
* loader
,
1615 int encoded_data_length
) {
1616 // Stream the data we received to the stream callback.
1617 stream_handler_
->DidStreamData(stream_handler_user_data_
,
1622 virtual void didFinishLoading(blink::WebURLLoader
* loader
,
1624 int64_t total_encoded_data_length
) {
1625 int32_t result
= success_
? PP_OK
: PP_ERROR_FAILED
;
1626 stream_handler_
->DidFinishStream(stream_handler_user_data_
, result
);
1630 virtual void didFail(blink::WebURLLoader
* loader
,
1631 const blink::WebURLError
& error
) {
1635 PP_Instance instance_
;
1636 scoped_ptr
<blink::WebURLLoader
> url_loader_
;
1637 std::string pexe_url_
;
1638 int32_t pexe_opt_level_
;
1639 const PPP_PexeStreamHandler
* stream_handler_
;
1640 void* stream_handler_user_data_
;
1642 int64_t expected_content_length_
;
1643 base::WeakPtrFactory
<PexeDownloader
> weak_factory_
;
1646 void StreamPexe(PP_Instance instance
,
1647 const char* pexe_url
,
1649 const PPP_PexeStreamHandler
* handler
,
1650 void* handler_user_data
) {
1651 content::PepperPluginInstance
* plugin_instance
=
1652 content::PepperPluginInstance::Get(instance
);
1653 if (!plugin_instance
) {
1654 base::MessageLoop::current()->PostTask(
1656 base::Bind(handler
->DidFinishStream
,
1658 static_cast<int32_t>(PP_ERROR_FAILED
)));
1662 GURL
gurl(pexe_url
);
1663 const blink::WebDocument
& document
=
1664 plugin_instance
->GetContainer()->element().document();
1665 scoped_ptr
<blink::WebURLLoader
> url_loader(
1666 CreateWebURLLoader(document
, gurl
));
1667 PexeDownloader
* downloader
= new PexeDownloader(instance
,
1674 blink::WebURLRequest url_request
= CreateWebURLRequest(document
, gurl
);
1675 // Mark the request as requesting a PNaCl bitcode file,
1676 // so that component updater can detect this user action.
1677 url_request
.addHTTPHeaderField(
1678 blink::WebString::fromUTF8("Accept"),
1679 blink::WebString::fromUTF8("application/x-pnacl, */*"));
1680 downloader
->Load(url_request
);
1683 const PPB_NaCl_Private nacl_interface
= {
1687 &Are3DInterfacesDisabled
,
1688 &BrokerDuplicateHandle
,
1689 &GetReadExecPnaclFd
,
1690 &CreateTemporaryFile
,
1691 &GetNumberOfProcessors
,
1692 &PPIsNonSFIModeEnabled
,
1693 &ReportTranslationFinished
,
1701 &NaClDebugEnabledForURL
,
1710 &RequestNaClManifest
,
1711 &GetManifestBaseURL
,
1712 &ProcessNaClManifest
,
1713 &DevInterfacesEnabled
,
1714 &ManifestGetProgramURL
,
1715 &GetPNaClResourceInfo
,
1716 &GetCpuFeatureAttrs
,
1717 &PostMessageToJavaScript
,
1719 &ReportSelLdrStatus
,
1728 const PPB_NaCl_Private
* GetNaClPrivateInterface() {
1729 return &nacl_interface
;