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"
15 #include "base/files/file.h"
16 #include "base/lazy_instance.h"
17 #include "base/logging.h"
18 #include "base/rand_util.h"
19 #include "base/strings/string_split.h"
20 #include "base/strings/string_util.h"
21 #include "components/nacl/common/nacl_host_messages.h"
22 #include "components/nacl/common/nacl_messages.h"
23 #include "components/nacl/common/nacl_nonsfi_util.h"
24 #include "components/nacl/common/nacl_switches.h"
25 #include "components/nacl/common/nacl_types.h"
26 #include "components/nacl/renderer/file_downloader.h"
27 #include "components/nacl/renderer/histogram.h"
28 #include "components/nacl/renderer/json_manifest.h"
29 #include "components/nacl/renderer/manifest_downloader.h"
30 #include "components/nacl/renderer/manifest_service_channel.h"
31 #include "components/nacl/renderer/nexe_load_manager.h"
32 #include "components/nacl/renderer/platform_info.h"
33 #include "components/nacl/renderer/pnacl_translation_resource_host.h"
34 #include "components/nacl/renderer/progress_event.h"
35 #include "components/nacl/renderer/trusted_plugin_channel.h"
36 #include "content/public/common/content_client.h"
37 #include "content/public/common/content_switches.h"
38 #include "content/public/common/sandbox_init.h"
39 #include "content/public/renderer/pepper_plugin_instance.h"
40 #include "content/public/renderer/render_thread.h"
41 #include "content/public/renderer/render_view.h"
42 #include "content/public/renderer/renderer_ppapi_host.h"
43 #include "native_client/src/public/imc_types.h"
44 #include "net/base/data_url.h"
45 #include "net/base/net_errors.h"
46 #include "net/http/http_util.h"
47 #include "ppapi/c/pp_bool.h"
48 #include "ppapi/c/private/pp_file_handle.h"
49 #include "ppapi/shared_impl/ppapi_globals.h"
50 #include "ppapi/shared_impl/ppapi_permissions.h"
51 #include "ppapi/shared_impl/ppapi_preferences.h"
52 #include "ppapi/shared_impl/var.h"
53 #include "ppapi/shared_impl/var_tracker.h"
54 #include "ppapi/thunk/enter.h"
55 #include "third_party/WebKit/public/platform/WebURLLoader.h"
56 #include "third_party/WebKit/public/platform/WebURLResponse.h"
57 #include "third_party/WebKit/public/web/WebDocument.h"
58 #include "third_party/WebKit/public/web/WebElement.h"
59 #include "third_party/WebKit/public/web/WebLocalFrame.h"
60 #include "third_party/WebKit/public/web/WebPluginContainer.h"
61 #include "third_party/WebKit/public/web/WebSecurityOrigin.h"
62 #include "third_party/WebKit/public/web/WebURLLoaderOptions.h"
63 #include "third_party/jsoncpp/source/include/json/reader.h"
64 #include "third_party/jsoncpp/source/include/json/value.h"
69 // The pseudo-architecture used to indicate portable native client.
70 const char* const kPortableArch
= "portable";
72 // The base URL for resources used by the PNaCl translator processes.
73 const char* kPNaClTranslatorBaseUrl
= "chrome://pnacl-translator/";
75 base::LazyInstance
<scoped_refptr
<PnaclTranslationResourceHost
> >
76 g_pnacl_resource_host
= LAZY_INSTANCE_INITIALIZER
;
78 bool InitializePnaclResourceHost() {
79 // Must run on the main thread.
80 content::RenderThread
* render_thread
= content::RenderThread::Get();
83 if (!g_pnacl_resource_host
.Get().get()) {
84 g_pnacl_resource_host
.Get() = new PnaclTranslationResourceHost(
85 render_thread
->GetIOMessageLoopProxy());
86 render_thread
->AddFilter(g_pnacl_resource_host
.Get().get());
92 InstanceInfo() : plugin_pid(base::kNullProcessId
), plugin_child_id(0) {}
94 ppapi::PpapiPermissions permissions
;
95 base::ProcessId plugin_pid
;
97 IPC::ChannelHandle channel_handle
;
100 typedef std::map
<PP_Instance
, InstanceInfo
> InstanceInfoMap
;
102 base::LazyInstance
<InstanceInfoMap
> g_instance_info
=
103 LAZY_INSTANCE_INITIALIZER
;
105 static const PP_NaClFileInfo kInvalidNaClFileInfo
= {
106 PP_kInvalidFileHandle
,
111 int GetRoutingID(PP_Instance instance
) {
112 // Check that we are on the main renderer thread.
113 DCHECK(content::RenderThread::Get());
114 content::RendererPpapiHost
*host
=
115 content::RendererPpapiHost::GetForPPInstance(instance
);
118 return host
->GetRoutingIDForWidget(instance
);
121 // Returns whether the channel_handle is valid or not.
122 bool IsValidChannelHandle(const IPC::ChannelHandle
& channel_handle
) {
123 if (channel_handle
.name
.empty()) {
127 #if defined(OS_POSIX)
128 if (channel_handle
.socket
.fd
== -1) {
136 void PostPPCompletionCallback(PP_CompletionCallback callback
,
138 ppapi::PpapiGlobals::Get()->GetMainThreadMessageLoop()->PostTask(
140 base::Bind(callback
.func
, callback
.user_data
, status
));
143 bool ManifestResolveKey(PP_Instance instance
,
144 bool is_helper_process
,
145 const std::string
& key
,
146 std::string
* full_url
,
147 PP_PNaClOptions
* pnacl_options
);
149 typedef base::Callback
<void(int32_t, const PP_NaClFileInfo
&)>
150 DownloadFileCallback
;
152 void DownloadFile(PP_Instance instance
,
153 const std::string
& url
,
154 const DownloadFileCallback
& callback
);
156 PP_Bool
StartPpapiProxy(PP_Instance instance
);
158 // Thin adapter from PPP_ManifestService to ManifestServiceChannel::Delegate.
159 // Note that user_data is managed by the caller of LaunchSelLdr. Please see
160 // also PP_ManifestService's comment for more details about resource
162 class ManifestServiceProxy
: public ManifestServiceChannel::Delegate
{
164 ManifestServiceProxy(PP_Instance pp_instance
)
165 : pp_instance_(pp_instance
) {
168 virtual ~ManifestServiceProxy() { }
170 virtual void StartupInitializationComplete() OVERRIDE
{
171 if (StartPpapiProxy(pp_instance_
) == PP_TRUE
) {
172 JsonManifest
* manifest
= GetJsonManifest(pp_instance_
);
173 NexeLoadManager
* load_manager
= NexeLoadManager::Get(pp_instance_
);
174 if (load_manager
&& manifest
) {
175 std::string full_url
;
176 PP_PNaClOptions pnacl_options
;
177 bool uses_nonsfi_mode
;
178 JsonManifest::ErrorInfo error_info
;
179 if (manifest
->GetProgramURL(&full_url
,
183 int64_t nexe_size
= load_manager
->nexe_size();
184 load_manager
->ReportLoadSuccess(full_url
, nexe_size
, nexe_size
);
190 virtual void OpenResource(
191 const std::string
& key
,
192 const ManifestServiceChannel::OpenResourceCallback
& callback
) OVERRIDE
{
193 DCHECK(ppapi::PpapiGlobals::Get()->GetMainThreadMessageLoop()->
194 BelongsToCurrentThread());
197 // TODO(teravest): Clean up pnacl_options logic in JsonManifest so we don't
198 // have to initialize it like this here.
199 PP_PNaClOptions pnacl_options
;
200 pnacl_options
.translate
= PP_FALSE
;
201 pnacl_options
.is_debug
= PP_FALSE
;
202 pnacl_options
.opt_level
= 2;
203 if (!ManifestResolveKey(pp_instance_
, false, key
, &url
, &pnacl_options
)) {
204 base::MessageLoop::current()->PostTask(
206 base::Bind(callback
, base::Passed(base::File()), 0, 0));
210 // We have to call DidDownloadFile, even if this object is destroyed, so
211 // that the handle inside PP_NaClFileInfo isn't leaked. This means that the
212 // callback passed to this function shouldn't have a weak pointer to an
215 // TODO(teravest): Make a type like PP_NaClFileInfo to use for DownloadFile
216 // that would close the file handle on destruction.
217 DownloadFile(pp_instance_
, url
,
218 base::Bind(&ManifestServiceProxy::DidDownloadFile
, callback
));
222 static void DidDownloadFile(
223 ManifestServiceChannel::OpenResourceCallback callback
,
225 const PP_NaClFileInfo
& file_info
) {
226 if (pp_error
!= PP_OK
) {
227 callback
.Run(base::File(), 0, 0);
230 callback
.Run(base::File(file_info
.handle
),
235 PP_Instance pp_instance_
;
236 DISALLOW_COPY_AND_ASSIGN(ManifestServiceProxy
);
239 blink::WebURLLoader
* CreateWebURLLoader(const blink::WebDocument
& document
,
241 blink::WebURLLoaderOptions options
;
242 options
.untrustedHTTP
= true;
244 // Options settings here follow the original behavior in the trusted
245 // plugin and PepperURLLoaderHost.
246 if (document
.securityOrigin().canRequest(gurl
)) {
247 options
.allowCredentials
= true;
250 options
.crossOriginRequestPolicy
=
251 blink::WebURLLoaderOptions::CrossOriginRequestPolicyUseAccessControl
;
253 return document
.frame()->createAssociatedURLLoader(options
);
256 blink::WebURLRequest
CreateWebURLRequest(const blink::WebDocument
& document
,
258 blink::WebURLRequest request
;
259 request
.initialize();
260 request
.setURL(gurl
);
261 request
.setFirstPartyForCookies(document
.firstPartyForCookies());
265 int32_t FileDownloaderToPepperError(FileDownloader::Status status
) {
267 case FileDownloader::SUCCESS
:
269 case FileDownloader::ACCESS_DENIED
:
270 return PP_ERROR_NOACCESS
;
271 case FileDownloader::FAILED
:
272 return PP_ERROR_FAILED
;
273 // No default case, to catch unhandled Status values.
275 return PP_ERROR_FAILED
;
278 // Launch NaCl's sel_ldr process.
279 void LaunchSelLdr(PP_Instance instance
,
280 PP_Bool main_service_runtime
,
281 const char* alleged_url
,
282 const PP_NaClFileInfo
* nexe_file_info
,
285 PP_Bool uses_nonsfi_mode
,
286 PP_Bool enable_ppapi_dev
,
287 PP_Bool enable_dyncode_syscalls
,
288 PP_Bool enable_exception_handling
,
289 PP_Bool enable_crash_throttling
,
291 PP_CompletionCallback callback
) {
292 CHECK(ppapi::PpapiGlobals::Get()->GetMainThreadMessageLoop()->
293 BelongsToCurrentThread());
295 // Create the manifest service proxy here, so on error case, it will be
296 // destructed (without passing it to ManifestServiceChannel).
297 scoped_ptr
<ManifestServiceChannel::Delegate
> manifest_service_proxy(
298 new ManifestServiceProxy(instance
));
300 FileDescriptor result_socket
;
301 IPC::Sender
* sender
= content::RenderThread::Get();
304 // If the nexe uses ppapi APIs, we need a routing ID.
305 // To get the routing ID, we must be on the main thread.
306 // Some nexes do not use ppapi and launch from the background thread,
307 // so those nexes can skip finding a routing_id.
309 routing_id
= GetRoutingID(instance
);
311 if (nexe_file_info
->handle
!= PP_kInvalidFileHandle
) {
312 base::File
closer(nexe_file_info
->handle
);
314 ppapi::PpapiGlobals::Get()->GetMainThreadMessageLoop()->PostTask(
316 base::Bind(callback
.func
, callback
.user_data
,
317 static_cast<int32_t>(PP_ERROR_FAILED
)));
322 InstanceInfo instance_info
;
323 instance_info
.url
= GURL(alleged_url
);
325 uint32_t perm_bits
= ppapi::PERMISSION_NONE
;
326 // Conditionally block 'Dev' interfaces. We do this for the NaCl process, so
327 // it's clearer to developers when they are using 'Dev' inappropriately. We
328 // must also check on the trusted side of the proxy.
329 if (enable_ppapi_dev
)
330 perm_bits
|= ppapi::PERMISSION_DEV
;
331 instance_info
.permissions
=
332 ppapi::PpapiPermissions::GetForCommandLine(perm_bits
);
333 std::string error_message_string
;
334 NaClLaunchResult launch_result
;
336 IPC::PlatformFileForTransit nexe_for_transit
=
337 IPC::InvalidPlatformFileForTransit();
338 #if defined(OS_POSIX)
339 if (nexe_file_info
->handle
!= PP_kInvalidFileHandle
)
340 nexe_for_transit
= base::FileDescriptor(nexe_file_info
->handle
, true);
341 #elif defined(OS_WIN)
342 // Duplicate the handle on the browser side instead of the renderer.
343 // This is because BrokerGetFileForProcess isn't part of content/public, and
344 // it's simpler to do the duplication in the browser anyway.
345 nexe_for_transit
= nexe_file_info
->handle
;
347 #error Unsupported target platform.
349 if (!sender
->Send(new NaClHostMsg_LaunchNaCl(
351 instance_info
.url
.spec(),
353 nexe_file_info
->token_lo
,
354 nexe_file_info
->token_hi
,
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
= NexeLoadManager::Get(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
= NexeLoadManager::Get(instance
);
398 DCHECK(load_manager
);
400 PostPPCompletionCallback(callback
, PP_ERROR_FAILED
);
401 base::SharedMemory::CloseHandle(launch_result
.crash_info_shmem_handle
);
405 // Store the crash information shared memory handle.
406 load_manager
->set_crash_info_shmem_handle(
407 launch_result
.crash_info_shmem_handle
);
409 // Create the trusted plugin channel.
410 if (IsValidChannelHandle(launch_result
.trusted_ipc_channel_handle
)) {
411 bool report_exit_status
= PP_ToBool(main_service_runtime
);
412 scoped_ptr
<TrustedPluginChannel
> trusted_plugin_channel(
413 new TrustedPluginChannel(
415 launch_result
.trusted_ipc_channel_handle
,
416 content::RenderThread::Get()->GetShutdownEvent(),
417 report_exit_status
));
418 load_manager
->set_trusted_plugin_channel(trusted_plugin_channel
.Pass());
420 PostPPCompletionCallback(callback
, PP_ERROR_FAILED
);
424 // Create the manifest service handle as well.
425 // For security hardening, disable the IPCs for open_resource() when they
426 // aren't needed. PNaCl doesn't expose open_resource(). Note that
427 // enable_dyncode_syscalls is true if and only if the plugin is a non-PNaCl
430 enable_dyncode_syscalls
&&
431 IsValidChannelHandle(
432 launch_result
.manifest_service_ipc_channel_handle
)) {
433 scoped_ptr
<ManifestServiceChannel
> manifest_service_channel(
434 new ManifestServiceChannel(
435 launch_result
.manifest_service_ipc_channel_handle
,
436 base::Bind(&PostPPCompletionCallback
, callback
),
437 manifest_service_proxy
.Pass(),
438 content::RenderThread::Get()->GetShutdownEvent()));
439 load_manager
->set_manifest_service_channel(
440 manifest_service_channel
.Pass());
442 // Currently, manifest service works only on linux/non-SFI mode.
443 // On other platforms, the socket will not be created, and thus this
444 // condition needs to be handled as success.
445 PostPPCompletionCallback(callback
, PP_OK
);
449 PP_Bool
StartPpapiProxy(PP_Instance instance
) {
450 NexeLoadManager
* load_manager
= NexeLoadManager::Get(instance
);
451 DCHECK(load_manager
);
455 content::PepperPluginInstance
* plugin_instance
=
456 content::PepperPluginInstance::Get(instance
);
457 if (!plugin_instance
) {
458 DLOG(ERROR
) << "GetInstance() failed";
462 InstanceInfoMap
& map
= g_instance_info
.Get();
463 InstanceInfoMap::iterator it
= map
.find(instance
);
464 if (it
== map
.end()) {
465 DLOG(ERROR
) << "Could not find instance ID";
468 InstanceInfo instance_info
= it
->second
;
471 PP_ExternalPluginResult result
= plugin_instance
->SwitchToOutOfProcessProxy(
472 base::FilePath().AppendASCII(instance_info
.url
.spec()),
473 instance_info
.permissions
,
474 instance_info
.channel_handle
,
475 instance_info
.plugin_pid
,
476 instance_info
.plugin_child_id
);
478 if (result
== PP_EXTERNAL_PLUGIN_OK
) {
479 // Log the amound of time that has passed between the trusted plugin being
480 // initialized and the untrusted plugin being initialized. This is
481 // (roughly) the cost of using NaCl, in terms of startup time.
482 load_manager
->ReportStartupOverhead();
484 } else if (result
== PP_EXTERNAL_PLUGIN_ERROR_MODULE
) {
485 load_manager
->ReportLoadError(PP_NACL_ERROR_START_PROXY_MODULE
,
486 "could not initialize module.");
487 } else if (result
== PP_EXTERNAL_PLUGIN_ERROR_INSTANCE
) {
488 load_manager
->ReportLoadError(PP_NACL_ERROR_START_PROXY_MODULE
,
489 "could not create instance.");
494 int UrandomFD(void) {
495 #if defined(OS_POSIX)
496 return base::GetUrandomFD();
502 PP_Bool
Are3DInterfacesDisabled() {
503 return PP_FromBool(CommandLine::ForCurrentProcess()->HasSwitch(
504 switches::kDisable3DAPIs
));
507 int32_t BrokerDuplicateHandle(PP_FileHandle source_handle
,
509 PP_FileHandle
* target_handle
,
510 uint32_t desired_access
,
513 return content::BrokerDuplicateHandle(source_handle
, process_id
,
514 target_handle
, desired_access
,
521 // Convert a URL to a filename for GetReadonlyPnaclFd.
522 // Must be kept in sync with PnaclCanOpenFile() in
523 // components/nacl/browser/nacl_file_host.cc.
524 std::string
PnaclComponentURLToFilename(const std::string
& url
) {
525 // PNaCl component URLs aren't arbitrary URLs; they are always either
526 // generated from ManifestResolveKey or PnaclResources::ReadResourceInfo.
527 // So, it's safe to just use string parsing operations here instead of
529 DCHECK(StartsWithASCII(url
, kPNaClTranslatorBaseUrl
, true));
530 std::string r
= url
.substr(std::string(kPNaClTranslatorBaseUrl
).length());
532 // Use white-listed-chars.
534 static const char* white_list
= "abcdefghijklmnopqrstuvwxyz0123456789_";
535 replace_pos
= r
.find_first_not_of(white_list
);
536 while(replace_pos
!= std::string::npos
) {
537 r
= r
.replace(replace_pos
, 1, "_");
538 replace_pos
= r
.find_first_not_of(white_list
);
543 PP_FileHandle
GetReadonlyPnaclFd(const char* url
,
546 uint64_t* nonce_hi
) {
547 std::string filename
= PnaclComponentURLToFilename(url
);
548 IPC::PlatformFileForTransit out_fd
= IPC::InvalidPlatformFileForTransit();
549 IPC::Sender
* sender
= content::RenderThread::Get();
551 if (!sender
->Send(new NaClHostMsg_GetReadonlyPnaclFD(
552 std::string(filename
), is_executable
,
553 &out_fd
, nonce_lo
, nonce_hi
))) {
554 return PP_kInvalidFileHandle
;
556 if (out_fd
== IPC::InvalidPlatformFileForTransit()) {
557 return PP_kInvalidFileHandle
;
559 return IPC::PlatformFileForTransitToPlatformFile(out_fd
);
562 void GetReadExecPnaclFd(const char* url
,
563 PP_NaClFileInfo
* out_file_info
) {
564 *out_file_info
= kInvalidNaClFileInfo
;
565 out_file_info
->handle
= GetReadonlyPnaclFd(url
, true /* is_executable */,
566 &out_file_info
->token_lo
,
567 &out_file_info
->token_hi
);
570 PP_FileHandle
CreateTemporaryFile(PP_Instance instance
) {
571 IPC::PlatformFileForTransit transit_fd
= IPC::InvalidPlatformFileForTransit();
572 IPC::Sender
* sender
= content::RenderThread::Get();
574 if (!sender
->Send(new NaClHostMsg_NaClCreateTemporaryFile(
576 return PP_kInvalidFileHandle
;
579 if (transit_fd
== IPC::InvalidPlatformFileForTransit()) {
580 return PP_kInvalidFileHandle
;
583 return IPC::PlatformFileForTransitToPlatformFile(transit_fd
);
586 int32_t GetNumberOfProcessors() {
587 IPC::Sender
* sender
= content::RenderThread::Get();
589 int32_t num_processors
= 1;
590 return sender
->Send(new NaClHostMsg_NaClGetNumProcessors(&num_processors
)) ?
594 PP_Bool
PPIsNonSFIModeEnabled() {
595 return PP_FromBool(IsNonSFIModeEnabled());
598 void GetNexeFd(PP_Instance instance
,
599 const std::string
& pexe_url
,
601 const base::Time
& last_modified_time
,
602 const std::string
& etag
,
603 bool has_no_store_header
,
604 base::Callback
<void(int32_t, bool, PP_FileHandle
)> callback
) {
605 if (!InitializePnaclResourceHost()) {
606 ppapi::PpapiGlobals::Get()->GetMainThreadMessageLoop()->PostTask(
609 static_cast<int32_t>(PP_ERROR_FAILED
),
611 PP_kInvalidFileHandle
));
615 PnaclCacheInfo cache_info
;
616 cache_info
.pexe_url
= GURL(pexe_url
);
617 // TODO(dschuff): Get this value from the pnacl json file after it
618 // rolls in from NaCl.
619 cache_info
.abi_version
= 1;
620 cache_info
.opt_level
= opt_level
;
621 cache_info
.last_modified
= last_modified_time
;
622 cache_info
.etag
= etag
;
623 cache_info
.has_no_store_header
= has_no_store_header
;
624 cache_info
.sandbox_isa
= GetSandboxArch();
625 cache_info
.extra_flags
= GetCpuFeatures();
627 g_pnacl_resource_host
.Get()->RequestNexeFd(
628 GetRoutingID(instance
),
634 void ReportTranslationFinished(PP_Instance instance
,
638 int64_t compile_time_us
) {
639 if (success
== PP_TRUE
) {
640 static const int32_t kUnknownOptLevel
= 4;
641 if (opt_level
< 0 || opt_level
> 3)
642 opt_level
= kUnknownOptLevel
;
643 HistogramEnumerate("NaCl.Options.PNaCl.OptLevel",
645 kUnknownOptLevel
+ 1);
646 HistogramKBPerSec("NaCl.Perf.PNaClLoadTime.CompileKBPerSec",
649 HistogramSizeKB("NaCl.Perf.Size.Pexe", pexe_size
/ 1024);
651 NexeLoadManager
* load_manager
= NexeLoadManager::Get(instance
);
653 base::TimeDelta total_time
= base::Time::Now() -
654 load_manager
->pnacl_start_time();
655 HistogramTimeTranslation("NaCl.Perf.PNaClLoadTime.TotalUncachedTime",
656 total_time
.InMilliseconds());
657 HistogramKBPerSec("NaCl.Perf.PNaClLoadTime.TotalUncachedKBPerSec",
659 total_time
.InMicroseconds());
663 // If the resource host isn't initialized, don't try to do that here.
664 // Just return because something is already very wrong.
665 if (g_pnacl_resource_host
.Get().get() == NULL
)
667 g_pnacl_resource_host
.Get()->ReportTranslationFinished(instance
, success
);
670 PP_FileHandle
OpenNaClExecutable(PP_Instance instance
,
671 const char* file_url
,
673 uint64_t* nonce_hi
) {
674 // Fast path only works for installed file URLs.
676 if (!gurl
.SchemeIs("chrome-extension"))
677 return PP_kInvalidFileHandle
;
679 content::PepperPluginInstance
* plugin_instance
=
680 content::PepperPluginInstance::Get(instance
);
681 if (!plugin_instance
)
682 return PP_kInvalidFileHandle
;
683 // IMPORTANT: Make sure the document can request the given URL. If we don't
684 // check, a malicious app could probe the extension system. This enforces a
685 // same-origin policy which prevents the app from requesting resources from
687 blink::WebSecurityOrigin security_origin
=
688 plugin_instance
->GetContainer()->element().document().securityOrigin();
689 if (!security_origin
.canRequest(gurl
))
690 return PP_kInvalidFileHandle
;
692 IPC::PlatformFileForTransit out_fd
= IPC::InvalidPlatformFileForTransit();
693 IPC::Sender
* sender
= content::RenderThread::Get();
697 base::FilePath file_path
;
699 new NaClHostMsg_OpenNaClExecutable(GetRoutingID(instance
),
704 return PP_kInvalidFileHandle
;
707 if (out_fd
== IPC::InvalidPlatformFileForTransit())
708 return PP_kInvalidFileHandle
;
710 return IPC::PlatformFileForTransitToPlatformFile(out_fd
);
713 void DispatchEvent(PP_Instance instance
,
714 PP_NaClEventType event_type
,
715 const char *resource_url
,
716 PP_Bool length_is_computable
,
717 uint64_t loaded_bytes
,
718 uint64_t total_bytes
) {
719 ProgressEvent
event(event_type
,
721 PP_ToBool(length_is_computable
),
724 DispatchProgressEvent(instance
, event
);
727 void ReportLoadSuccess(PP_Instance instance
,
728 uint64_t loaded_bytes
,
729 uint64_t total_bytes
) {
730 NexeLoadManager
* load_manager
= NexeLoadManager::Get(instance
);
732 load_manager
->ReportLoadSuccess(load_manager
->program_url(),
738 void ReportLoadError(PP_Instance instance
,
740 const char* error_message
) {
741 NexeLoadManager
* load_manager
= NexeLoadManager::Get(instance
);
743 load_manager
->ReportLoadError(error
, error_message
);
746 void ReportLoadAbort(PP_Instance instance
) {
747 NexeLoadManager
* load_manager
= NexeLoadManager::Get(instance
);
749 load_manager
->ReportLoadAbort();
752 void InstanceCreated(PP_Instance instance
) {
753 NexeLoadManager::Create(instance
);
756 void InstanceDestroyed(PP_Instance instance
) {
757 DeleteJsonManifest(instance
);
758 NexeLoadManager::Delete(instance
);
761 PP_Bool
NaClDebugEnabledForURL(const char* alleged_nmf_url
) {
762 if (!CommandLine::ForCurrentProcess()->HasSwitch(switches::kEnableNaClDebug
))
764 IPC::Sender
* sender
= content::RenderThread::Get();
766 bool should_debug
= false;
768 sender
->Send(new NaClHostMsg_NaClDebugEnabledForURL(GURL(alleged_nmf_url
),
773 void LogToConsole(PP_Instance instance
, const char* message
) {
774 NexeLoadManager
* load_manager
= NexeLoadManager::Get(instance
);
775 DCHECK(load_manager
);
777 load_manager
->LogToConsole(std::string(message
));
780 PP_NaClReadyState
GetNaClReadyState(PP_Instance instance
) {
781 NexeLoadManager
* load_manager
= NexeLoadManager::Get(instance
);
782 DCHECK(load_manager
);
784 return load_manager
->nacl_ready_state();
785 return PP_NACL_READY_STATE_UNSENT
;
788 void Vlog(const char* message
) {
792 void InitializePlugin(PP_Instance instance
,
795 const char* argv
[]) {
796 NexeLoadManager
* load_manager
= NexeLoadManager::Get(instance
);
797 DCHECK(load_manager
);
799 load_manager
->InitializePlugin(argc
, argn
, argv
);
802 int64_t GetNexeSize(PP_Instance instance
) {
803 NexeLoadManager
* load_manager
= NexeLoadManager::Get(instance
);
804 DCHECK(load_manager
);
806 return load_manager
->nexe_size();
810 void DownloadManifestToBuffer(PP_Instance instance
,
811 struct PP_CompletionCallback callback
);
813 bool CreateJsonManifest(PP_Instance instance
,
814 const std::string
& manifest_url
,
815 const std::string
& manifest_data
);
817 void RequestNaClManifest(PP_Instance instance
,
818 PP_CompletionCallback callback
) {
819 NexeLoadManager
* load_manager
= NexeLoadManager::Get(instance
);
820 DCHECK(load_manager
);
822 ppapi::PpapiGlobals::Get()->GetMainThreadMessageLoop()->PostTask(
824 base::Bind(callback
.func
, callback
.user_data
,
825 static_cast<int32_t>(PP_ERROR_FAILED
)));
829 std::string url
= load_manager
->GetManifestURLArgument();
830 if (url
.empty() || !load_manager
->RequestNaClManifest(url
)) {
831 ppapi::PpapiGlobals::Get()->GetMainThreadMessageLoop()->PostTask(
833 base::Bind(callback
.func
, callback
.user_data
,
834 static_cast<int32_t>(PP_ERROR_FAILED
)));
838 const GURL
& base_url
= load_manager
->manifest_base_url();
839 if (base_url
.SchemeIs("data")) {
841 std::string mime_type
;
844 int32_t error
= PP_ERROR_FAILED
;
845 if (net::DataURL::Parse(gurl
, &mime_type
, &charset
, &data
)) {
846 if (data
.size() <= ManifestDownloader::kNaClManifestMaxFileBytes
) {
847 if (CreateJsonManifest(instance
, base_url
.spec(), data
))
850 load_manager
->ReportLoadError(PP_NACL_ERROR_MANIFEST_TOO_LARGE
,
851 "manifest file too large.");
854 load_manager
->ReportLoadError(PP_NACL_ERROR_MANIFEST_LOAD_URL
,
855 "could not load manifest url.");
857 ppapi::PpapiGlobals::Get()->GetMainThreadMessageLoop()->PostTask(
859 base::Bind(callback
.func
, callback
.user_data
, error
));
861 DownloadManifestToBuffer(instance
, callback
);
865 PP_Var
GetManifestBaseURL(PP_Instance instance
) {
866 NexeLoadManager
* load_manager
= NexeLoadManager::Get(instance
);
867 DCHECK(load_manager
);
869 return PP_MakeUndefined();
870 const GURL
& gurl
= load_manager
->manifest_base_url();
871 if (!gurl
.is_valid())
872 return PP_MakeUndefined();
873 return ppapi::StringVar::StringToPPVar(gurl
.spec());
876 void ProcessNaClManifest(PP_Instance instance
, const char* program_url
) {
877 nacl::NexeLoadManager
* load_manager
= NexeLoadManager::Get(instance
);
879 load_manager
->ProcessNaClManifest(program_url
);
882 PP_Bool
DevInterfacesEnabled(PP_Instance instance
) {
883 nacl::NexeLoadManager
* load_manager
= NexeLoadManager::Get(instance
);
885 return PP_FromBool(load_manager
->DevInterfacesEnabled());
889 void DownloadManifestToBufferCompletion(PP_Instance instance
,
890 struct PP_CompletionCallback callback
,
891 base::Time start_time
,
892 PP_NaClError pp_nacl_error
,
893 const std::string
& data
);
895 void DownloadManifestToBuffer(PP_Instance instance
,
896 struct PP_CompletionCallback callback
) {
897 nacl::NexeLoadManager
* load_manager
= NexeLoadManager::Get(instance
);
898 DCHECK(load_manager
);
899 content::PepperPluginInstance
* plugin_instance
=
900 content::PepperPluginInstance::Get(instance
);
901 if (!load_manager
|| !plugin_instance
) {
902 ppapi::PpapiGlobals::Get()->GetMainThreadMessageLoop()->PostTask(
904 base::Bind(callback
.func
, callback
.user_data
,
905 static_cast<int32_t>(PP_ERROR_FAILED
)));
907 const blink::WebDocument
& document
=
908 plugin_instance
->GetContainer()->element().document();
910 const GURL
& gurl
= load_manager
->manifest_base_url();
911 scoped_ptr
<blink::WebURLLoader
> url_loader(
912 CreateWebURLLoader(document
, gurl
));
913 blink::WebURLRequest request
= CreateWebURLRequest(document
, gurl
);
915 // ManifestDownloader deletes itself after invoking the callback.
916 ManifestDownloader
* manifest_downloader
= new ManifestDownloader(
918 load_manager
->is_installed(),
919 base::Bind(DownloadManifestToBufferCompletion
,
920 instance
, callback
, base::Time::Now()));
921 manifest_downloader
->Load(request
);
924 void DownloadManifestToBufferCompletion(PP_Instance instance
,
925 struct PP_CompletionCallback callback
,
926 base::Time start_time
,
927 PP_NaClError pp_nacl_error
,
928 const std::string
& data
) {
929 base::TimeDelta download_time
= base::Time::Now() - start_time
;
930 HistogramTimeSmall("NaCl.Perf.StartupTime.ManifestDownload",
931 download_time
.InMilliseconds());
933 nacl::NexeLoadManager
* load_manager
= NexeLoadManager::Get(instance
);
935 callback
.func(callback
.user_data
, PP_ERROR_ABORTED
);
940 switch (pp_nacl_error
) {
941 case PP_NACL_ERROR_LOAD_SUCCESS
:
944 case PP_NACL_ERROR_MANIFEST_LOAD_URL
:
945 pp_error
= PP_ERROR_FAILED
;
946 load_manager
->ReportLoadError(PP_NACL_ERROR_MANIFEST_LOAD_URL
,
947 "could not load manifest url.");
949 case PP_NACL_ERROR_MANIFEST_TOO_LARGE
:
950 pp_error
= PP_ERROR_FILETOOBIG
;
951 load_manager
->ReportLoadError(PP_NACL_ERROR_MANIFEST_TOO_LARGE
,
952 "manifest file too large.");
954 case PP_NACL_ERROR_MANIFEST_NOACCESS_URL
:
955 pp_error
= PP_ERROR_NOACCESS
;
956 load_manager
->ReportLoadError(PP_NACL_ERROR_MANIFEST_NOACCESS_URL
,
957 "access to manifest url was denied.");
961 pp_error
= PP_ERROR_FAILED
;
962 load_manager
->ReportLoadError(PP_NACL_ERROR_MANIFEST_LOAD_URL
,
963 "could not load manifest url.");
966 if (pp_error
== PP_OK
) {
967 std::string base_url
= load_manager
->manifest_base_url().spec();
968 if (!CreateJsonManifest(instance
, base_url
, data
))
969 pp_error
= PP_ERROR_FAILED
;
971 callback
.func(callback
.user_data
, pp_error
);
974 bool CreateJsonManifest(PP_Instance instance
,
975 const std::string
& manifest_url
,
976 const std::string
& manifest_data
) {
977 HistogramSizeKB("NaCl.Perf.Size.Manifest",
978 static_cast<int32_t>(manifest_data
.length() / 1024));
980 nacl::NexeLoadManager
* load_manager
= NexeLoadManager::Get(instance
);
984 const char* isa_type
;
985 if (load_manager
->IsPNaCl())
986 isa_type
= kPortableArch
;
988 isa_type
= GetSandboxArch();
990 scoped_ptr
<nacl::JsonManifest
> j(
991 new nacl::JsonManifest(
992 manifest_url
.c_str(),
994 IsNonSFIModeEnabled(),
995 PP_ToBool(NaClDebugEnabledForURL(manifest_url
.c_str()))));
996 JsonManifest::ErrorInfo error_info
;
997 if (j
->Init(manifest_data
.c_str(), &error_info
)) {
998 AddJsonManifest(instance
, j
.Pass());
1001 load_manager
->ReportLoadError(error_info
.error
, error_info
.string
);
1005 PP_Bool
ManifestGetProgramURL(PP_Instance instance
,
1006 PP_Var
* pp_full_url
,
1007 PP_PNaClOptions
* pnacl_options
,
1008 PP_Bool
* pp_uses_nonsfi_mode
) {
1009 nacl::NexeLoadManager
* load_manager
= NexeLoadManager::Get(instance
);
1011 JsonManifest
* manifest
= GetJsonManifest(instance
);
1012 if (manifest
== NULL
)
1015 bool uses_nonsfi_mode
;
1016 std::string full_url
;
1017 JsonManifest::ErrorInfo error_info
;
1018 if (manifest
->GetProgramURL(&full_url
, pnacl_options
, &uses_nonsfi_mode
,
1020 *pp_full_url
= ppapi::StringVar::StringToPPVar(full_url
);
1021 *pp_uses_nonsfi_mode
= PP_FromBool(uses_nonsfi_mode
);
1026 load_manager
->ReportLoadError(error_info
.error
, error_info
.string
);
1030 bool ManifestResolveKey(PP_Instance instance
,
1031 bool is_helper_process
,
1032 const std::string
& key
,
1033 std::string
* full_url
,
1034 PP_PNaClOptions
* pnacl_options
) {
1035 // For "helper" processes (llc and ld, for PNaCl translation), we resolve
1036 // keys manually as there is no existing .nmf file to parse.
1037 if (is_helper_process
) {
1038 pnacl_options
->translate
= PP_FALSE
;
1039 // We can only resolve keys in the files/ namespace.
1040 const std::string kFilesPrefix
= "files/";
1041 if (key
.find(kFilesPrefix
) == std::string::npos
) {
1042 nacl::NexeLoadManager
* load_manager
= NexeLoadManager::Get(instance
);
1044 load_manager
->ReportLoadError(PP_NACL_ERROR_MANIFEST_RESOLVE_URL
,
1045 "key did not start with files/");
1048 std::string key_basename
= key
.substr(kFilesPrefix
.length());
1049 *full_url
= std::string(kPNaClTranslatorBaseUrl
) + GetSandboxArch() + "/" +
1054 JsonManifest
* manifest
= GetJsonManifest(instance
);
1055 if (manifest
== NULL
)
1058 return manifest
->ResolveKey(key
, full_url
, pnacl_options
);
1061 PP_Bool
GetPNaClResourceInfo(PP_Instance instance
,
1062 PP_Var
* llc_tool_name
,
1063 PP_Var
* ld_tool_name
) {
1064 static const char kFilename
[] = "chrome://pnacl-translator/pnacl.json";
1065 NexeLoadManager
* load_manager
= NexeLoadManager::Get(instance
);
1066 DCHECK(load_manager
);
1070 uint64_t nonce_lo
= 0;
1071 uint64_t nonce_hi
= 0;
1072 base::File
file(GetReadonlyPnaclFd(kFilename
, false /* is_executable */,
1073 &nonce_lo
, &nonce_hi
));
1074 if (!file
.IsValid()) {
1075 load_manager
->ReportLoadError(
1076 PP_NACL_ERROR_PNACL_RESOURCE_FETCH
,
1077 "The Portable Native Client (pnacl) component is not "
1078 "installed. Please consult chrome://components for more "
1083 base::File::Info file_info
;
1084 if (!file
.GetInfo(&file_info
)) {
1085 load_manager
->ReportLoadError(
1086 PP_NACL_ERROR_PNACL_RESOURCE_FETCH
,
1087 std::string("GetPNaClResourceInfo, GetFileInfo failed for: ") +
1092 if (file_info
.size
> 1 << 20) {
1093 load_manager
->ReportLoadError(
1094 PP_NACL_ERROR_PNACL_RESOURCE_FETCH
,
1095 std::string("GetPNaClResourceInfo, file too large: ") + kFilename
);
1099 scoped_ptr
<char[]> buffer(new char[file_info
.size
+ 1]);
1100 if (buffer
.get() == NULL
) {
1101 load_manager
->ReportLoadError(
1102 PP_NACL_ERROR_PNACL_RESOURCE_FETCH
,
1103 std::string("GetPNaClResourceInfo, couldn't allocate for: ") +
1108 int rc
= file
.Read(0, buffer
.get(), file_info
.size
);
1110 load_manager
->ReportLoadError(
1111 PP_NACL_ERROR_PNACL_RESOURCE_FETCH
,
1112 std::string("GetPNaClResourceInfo, reading failed for: ") + kFilename
);
1116 // Null-terminate the bytes we we read from the file.
1117 buffer
.get()[rc
] = 0;
1119 // Expect the JSON file to contain a top-level object (dictionary).
1120 Json::Reader json_reader
;
1121 Json::Value json_data
;
1122 if (!json_reader
.parse(buffer
.get(), json_data
)) {
1123 load_manager
->ReportLoadError(
1124 PP_NACL_ERROR_PNACL_RESOURCE_FETCH
,
1125 std::string("Parsing resource info failed: JSON parse error: ") +
1126 json_reader
.getFormattedErrorMessages());
1130 if (!json_data
.isObject()) {
1131 load_manager
->ReportLoadError(
1132 PP_NACL_ERROR_PNACL_RESOURCE_FETCH
,
1133 "Parsing resource info failed: Malformed JSON dictionary");
1137 if (json_data
.isMember("pnacl-llc-name")) {
1138 Json::Value json_name
= json_data
["pnacl-llc-name"];
1139 if (json_name
.isString()) {
1140 std::string llc_tool_name_str
= json_name
.asString();
1141 *llc_tool_name
= ppapi::StringVar::StringToPPVar(llc_tool_name_str
);
1145 if (json_data
.isMember("pnacl-ld-name")) {
1146 Json::Value json_name
= json_data
["pnacl-ld-name"];
1147 if (json_name
.isString()) {
1148 std::string ld_tool_name_str
= json_name
.asString();
1149 *ld_tool_name
= ppapi::StringVar::StringToPPVar(ld_tool_name_str
);
1155 PP_Var
GetCpuFeatureAttrs() {
1156 return ppapi::StringVar::StringToPPVar(GetCpuFeatures());
1159 // Encapsulates some of the state for a call to DownloadNexe to prevent
1160 // argument lists from getting too long.
1161 struct DownloadNexeRequest
{
1162 PP_Instance instance
;
1164 PP_CompletionCallback callback
;
1165 base::Time start_time
;
1168 // A utility class to ensure that we don't send progress events more often than
1169 // every 10ms for a given file.
1170 class ProgressEventRateLimiter
{
1172 explicit ProgressEventRateLimiter(PP_Instance instance
)
1173 : instance_(instance
) { }
1175 void ReportProgress(const std::string
& url
,
1176 int64_t total_bytes_received
,
1177 int64_t total_bytes_to_be_received
) {
1178 base::Time now
= base::Time::Now();
1179 if (now
- last_event_
> base::TimeDelta::FromMilliseconds(10)) {
1180 DispatchProgressEvent(instance_
,
1181 ProgressEvent(PP_NACL_EVENT_PROGRESS
,
1183 total_bytes_to_be_received
>= 0,
1184 total_bytes_received
,
1185 total_bytes_to_be_received
));
1191 PP_Instance instance_
;
1192 base::Time last_event_
;
1195 void DownloadNexeCompletion(const DownloadNexeRequest
& request
,
1196 PP_NaClFileInfo
* out_file_info
,
1197 FileDownloader::Status status
,
1198 base::File target_file
,
1201 void DownloadNexe(PP_Instance instance
,
1203 PP_NaClFileInfo
* out_file_info
,
1204 PP_CompletionCallback callback
) {
1206 CHECK(out_file_info
);
1207 DownloadNexeRequest request
;
1208 request
.instance
= instance
;
1210 request
.callback
= callback
;
1211 request
.start_time
= base::Time::Now();
1213 // Try the fast path for retrieving the file first.
1214 PP_FileHandle handle
= OpenNaClExecutable(instance
,
1216 &out_file_info
->token_lo
,
1217 &out_file_info
->token_hi
);
1218 if (handle
!= PP_kInvalidFileHandle
) {
1219 DownloadNexeCompletion(request
,
1221 FileDownloader::SUCCESS
,
1227 // The fast path didn't work, we'll fetch the file using URLLoader and write
1228 // it to local storage.
1229 base::File
target_file(CreateTemporaryFile(instance
));
1232 content::PepperPluginInstance
* plugin_instance
=
1233 content::PepperPluginInstance::Get(instance
);
1234 if (!plugin_instance
) {
1235 ppapi::PpapiGlobals::Get()->GetMainThreadMessageLoop()->PostTask(
1237 base::Bind(callback
.func
, callback
.user_data
,
1238 static_cast<int32_t>(PP_ERROR_FAILED
)));
1240 const blink::WebDocument
& document
=
1241 plugin_instance
->GetContainer()->element().document();
1242 scoped_ptr
<blink::WebURLLoader
> url_loader(
1243 CreateWebURLLoader(document
, gurl
));
1244 blink::WebURLRequest url_request
= CreateWebURLRequest(document
, gurl
);
1246 ProgressEventRateLimiter
* tracker
= new ProgressEventRateLimiter(instance
);
1248 // FileDownloader deletes itself after invoking DownloadNexeCompletion.
1249 FileDownloader
* file_downloader
= new FileDownloader(
1252 base::Bind(&DownloadNexeCompletion
, request
, out_file_info
),
1253 base::Bind(&ProgressEventRateLimiter::ReportProgress
,
1254 base::Owned(tracker
), std::string(url
)));
1255 file_downloader
->Load(url_request
);
1258 void DownloadNexeCompletion(const DownloadNexeRequest
& request
,
1259 PP_NaClFileInfo
* out_file_info
,
1260 FileDownloader::Status status
,
1261 base::File target_file
,
1263 int32_t pp_error
= FileDownloaderToPepperError(status
);
1264 int64_t bytes_read
= -1;
1265 if (pp_error
== PP_OK
&& target_file
.IsValid()) {
1266 base::File::Info info
;
1267 if (target_file
.GetInfo(&info
))
1268 bytes_read
= info
.size
;
1271 if (bytes_read
== -1) {
1272 target_file
.Close();
1273 pp_error
= PP_ERROR_FAILED
;
1276 base::TimeDelta download_time
= base::Time::Now() - request
.start_time
;
1278 NexeLoadManager
* load_manager
= NexeLoadManager::Get(request
.instance
);
1280 load_manager
->NexeFileDidOpen(pp_error
,
1288 if (pp_error
== PP_OK
&& target_file
.IsValid())
1289 out_file_info
->handle
= target_file
.TakePlatformFile();
1291 out_file_info
->handle
= PP_kInvalidFileHandle
;
1293 request
.callback
.func(request
.callback
.user_data
, pp_error
);
1296 void DownloadFileCompletion(
1297 const DownloadFileCallback
& callback
,
1298 FileDownloader::Status status
,
1301 int32_t pp_error
= FileDownloaderToPepperError(status
);
1302 PP_NaClFileInfo file_info
;
1303 if (pp_error
== PP_OK
) {
1304 file_info
.handle
= file
.TakePlatformFile();
1305 file_info
.token_lo
= 0;
1306 file_info
.token_hi
= 0;
1308 file_info
= kInvalidNaClFileInfo
;
1311 callback
.Run(pp_error
, file_info
);
1314 void DownloadFile(PP_Instance instance
,
1315 const std::string
& url
,
1316 const DownloadFileCallback
& callback
) {
1317 DCHECK(ppapi::PpapiGlobals::Get()->GetMainThreadMessageLoop()->
1318 BelongsToCurrentThread());
1320 NexeLoadManager
* load_manager
= NexeLoadManager::Get(instance
);
1321 DCHECK(load_manager
);
1322 if (!load_manager
) {
1323 base::MessageLoop::current()->PostTask(
1325 base::Bind(callback
,
1326 static_cast<int32_t>(PP_ERROR_FAILED
),
1327 kInvalidNaClFileInfo
));
1331 // Handle special PNaCl support files which are installed on the user's
1333 if (url
.find(kPNaClTranslatorBaseUrl
, 0) == 0) {
1334 PP_NaClFileInfo file_info
= kInvalidNaClFileInfo
;
1335 PP_FileHandle handle
= GetReadonlyPnaclFd(url
.c_str(),
1336 false /* is_executable */,
1337 &file_info
.token_lo
,
1338 &file_info
.token_hi
);
1339 if (handle
== PP_kInvalidFileHandle
) {
1340 base::MessageLoop::current()->PostTask(
1342 base::Bind(callback
,
1343 static_cast<int32_t>(PP_ERROR_FAILED
),
1344 kInvalidNaClFileInfo
));
1347 file_info
.handle
= handle
;
1348 base::MessageLoop::current()->PostTask(
1350 base::Bind(callback
, static_cast<int32_t>(PP_OK
), file_info
));
1354 // We have to ensure that this url resolves relative to the plugin base url
1355 // before downloading it.
1356 const GURL
& test_gurl
= load_manager
->plugin_base_url().Resolve(url
);
1357 if (!test_gurl
.is_valid()) {
1358 base::MessageLoop::current()->PostTask(
1360 base::Bind(callback
,
1361 static_cast<int32_t>(PP_ERROR_FAILED
),
1362 kInvalidNaClFileInfo
));
1366 // Try the fast path for retrieving the file first.
1367 uint64_t file_token_lo
= 0;
1368 uint64_t file_token_hi
= 0;
1369 PP_FileHandle file_handle
= OpenNaClExecutable(instance
,
1373 if (file_handle
!= PP_kInvalidFileHandle
) {
1374 PP_NaClFileInfo file_info
;
1375 file_info
.handle
= file_handle
;
1376 file_info
.token_lo
= file_token_lo
;
1377 file_info
.token_hi
= file_token_hi
;
1378 base::MessageLoop::current()->PostTask(
1380 base::Bind(callback
, static_cast<int32_t>(PP_OK
), file_info
));
1384 // The fast path didn't work, we'll fetch the file using URLLoader and write
1385 // it to local storage.
1386 base::File
target_file(CreateTemporaryFile(instance
));
1389 content::PepperPluginInstance
* plugin_instance
=
1390 content::PepperPluginInstance::Get(instance
);
1391 if (!plugin_instance
) {
1392 base::MessageLoop::current()->PostTask(
1394 base::Bind(callback
,
1395 static_cast<int32_t>(PP_ERROR_FAILED
),
1396 kInvalidNaClFileInfo
));
1398 const blink::WebDocument
& document
=
1399 plugin_instance
->GetContainer()->element().document();
1400 scoped_ptr
<blink::WebURLLoader
> url_loader(
1401 CreateWebURLLoader(document
, gurl
));
1402 blink::WebURLRequest url_request
= CreateWebURLRequest(document
, gurl
);
1404 ProgressEventRateLimiter
* tracker
= new ProgressEventRateLimiter(instance
);
1406 // FileDownloader deletes itself after invoking DownloadNexeCompletion.
1407 FileDownloader
* file_downloader
= new FileDownloader(
1410 base::Bind(&DownloadFileCompletion
, callback
),
1411 base::Bind(&ProgressEventRateLimiter::ReportProgress
,
1412 base::Owned(tracker
), std::string(url
)));
1413 file_downloader
->Load(url_request
);
1416 void ReportSelLdrStatus(PP_Instance instance
,
1417 int32_t load_status
,
1418 int32_t max_status
) {
1419 HistogramEnumerate("NaCl.LoadStatus.SelLdr", load_status
, max_status
);
1420 NexeLoadManager
* load_manager
= NexeLoadManager::Get(instance
);
1421 DCHECK(load_manager
);
1425 // Gather data to see if being installed changes load outcomes.
1426 const char* name
= load_manager
->is_installed() ?
1427 "NaCl.LoadStatus.SelLdr.InstalledApp" :
1428 "NaCl.LoadStatus.SelLdr.NotInstalledApp";
1429 HistogramEnumerate(name
, load_status
, max_status
);
1432 void LogTranslateTime(const char* histogram_name
,
1433 int64_t time_in_us
) {
1434 ppapi::PpapiGlobals::Get()->GetMainThreadMessageLoop()->PostTask(
1436 base::Bind(&HistogramTimeTranslation
,
1437 std::string(histogram_name
),
1438 time_in_us
/ 1000));
1441 void DidOpenManifestEntry(PP_NaClFileInfo
* out_file_info
,
1442 PP_CompletionCallback callback
,
1444 const PP_NaClFileInfo
& file_info
) {
1445 if (pp_error
== PP_OK
)
1446 *out_file_info
= file_info
;
1447 callback
.func(callback
.user_data
, pp_error
);
1450 void OpenManifestEntry(PP_Instance instance
,
1451 PP_Bool is_helper_process
,
1453 PP_NaClFileInfo
* out_file_info
,
1454 PP_CompletionCallback callback
) {
1456 PP_PNaClOptions pnacl_options
;
1457 pnacl_options
.translate
= PP_FALSE
;
1458 pnacl_options
.is_debug
= PP_FALSE
;
1459 pnacl_options
.opt_level
= 2;
1460 if (!ManifestResolveKey(instance
,
1461 PP_ToBool(is_helper_process
),
1465 PostPPCompletionCallback(callback
, PP_ERROR_FAILED
);
1468 // TODO(teravest): Make a type like PP_NaClFileInfo to use for DownloadFile
1469 // that would close the file handle on destruction.
1470 DownloadFile(instance
, url
,
1471 base::Bind(&DidOpenManifestEntry
, out_file_info
, callback
));
1474 void SetPNaClStartTime(PP_Instance instance
) {
1475 NexeLoadManager
* load_manager
= NexeLoadManager::Get(instance
);
1477 load_manager
->set_pnacl_start_time(base::Time::Now());
1480 // PexeDownloader is responsible for deleting itself when the download
1482 class PexeDownloader
: public blink::WebURLLoaderClient
{
1484 PexeDownloader(PP_Instance instance
,
1485 scoped_ptr
<blink::WebURLLoader
> url_loader
,
1486 const std::string
& pexe_url
,
1487 int32_t pexe_opt_level
,
1488 const PPP_PexeStreamHandler
* stream_handler
,
1489 void* stream_handler_user_data
)
1490 : instance_(instance
),
1491 url_loader_(url_loader
.Pass()),
1492 pexe_url_(pexe_url
),
1493 pexe_opt_level_(pexe_opt_level
),
1494 stream_handler_(stream_handler
),
1495 stream_handler_user_data_(stream_handler_user_data
),
1497 expected_content_length_(-1),
1498 weak_factory_(this) { }
1500 void Load(const blink::WebURLRequest
& request
) {
1501 url_loader_
->loadAsynchronously(request
, this);
1505 virtual void didReceiveResponse(blink::WebURLLoader
* loader
,
1506 const blink::WebURLResponse
& response
) {
1507 success_
= (response
.httpStatusCode() == 200);
1511 expected_content_length_
= response
.expectedContentLength();
1513 // Defer loading after receiving headers. This is because we may already
1514 // have a cached translated nexe, so check for that now.
1515 url_loader_
->setDefersLoading(true);
1517 std::string etag
= response
.httpHeaderField("etag").utf8();
1518 std::string last_modified
=
1519 response
.httpHeaderField("last-modified").utf8();
1520 base::Time last_modified_time
;
1521 base::Time::FromString(last_modified
.c_str(), &last_modified_time
);
1523 bool has_no_store_header
= false;
1524 std::string cache_control
=
1525 response
.httpHeaderField("cache-control").utf8();
1527 std::vector
<std::string
> values
;
1528 base::SplitString(cache_control
, ',', &values
);
1529 for (std::vector
<std::string
>::const_iterator it
= values
.begin();
1532 if (base::StringToLowerASCII(*it
) == "no-store")
1533 has_no_store_header
= true;
1536 GetNexeFd(instance_
,
1541 has_no_store_header
,
1542 base::Bind(&PexeDownloader::didGetNexeFd
,
1543 weak_factory_
.GetWeakPtr()));
1546 virtual void didGetNexeFd(int32_t pp_error
,
1548 PP_FileHandle file_handle
) {
1549 if (!content::PepperPluginInstance::Get(instance_
)) {
1554 HistogramEnumerate("NaCl.Perf.PNaClCache.IsHit", cache_hit
, 2);
1556 stream_handler_
->DidCacheHit(stream_handler_user_data_
, file_handle
);
1558 // We delete the PexeDownloader at this point since we successfully got a
1559 // cached, translated nexe.
1563 stream_handler_
->DidCacheMiss(stream_handler_user_data_
,
1564 expected_content_length_
,
1567 // No translated nexe was found in the cache, so we should download the
1568 // file to start streaming it.
1569 url_loader_
->setDefersLoading(false);
1572 virtual void didReceiveData(blink::WebURLLoader
* loader
,
1575 int encoded_data_length
) {
1576 if (content::PepperPluginInstance::Get(instance_
)) {
1577 // Stream the data we received to the stream callback.
1578 stream_handler_
->DidStreamData(stream_handler_user_data_
,
1584 virtual void didFinishLoading(blink::WebURLLoader
* loader
,
1586 int64_t total_encoded_data_length
) {
1587 int32_t result
= success_
? PP_OK
: PP_ERROR_FAILED
;
1589 if (content::PepperPluginInstance::Get(instance_
))
1590 stream_handler_
->DidFinishStream(stream_handler_user_data_
, result
);
1594 virtual void didFail(blink::WebURLLoader
* loader
,
1595 const blink::WebURLError
& error
) {
1599 PP_Instance instance_
;
1600 scoped_ptr
<blink::WebURLLoader
> url_loader_
;
1601 std::string pexe_url_
;
1602 int32_t pexe_opt_level_
;
1603 const PPP_PexeStreamHandler
* stream_handler_
;
1604 void* stream_handler_user_data_
;
1606 int64_t expected_content_length_
;
1607 base::WeakPtrFactory
<PexeDownloader
> weak_factory_
;
1610 void StreamPexe(PP_Instance instance
,
1611 const char* pexe_url
,
1613 const PPP_PexeStreamHandler
* handler
,
1614 void* handler_user_data
) {
1615 content::PepperPluginInstance
* plugin_instance
=
1616 content::PepperPluginInstance::Get(instance
);
1617 if (!plugin_instance
) {
1618 base::MessageLoop::current()->PostTask(
1620 base::Bind(handler
->DidFinishStream
,
1622 static_cast<int32_t>(PP_ERROR_FAILED
)));
1626 GURL
gurl(pexe_url
);
1627 const blink::WebDocument
& document
=
1628 plugin_instance
->GetContainer()->element().document();
1629 scoped_ptr
<blink::WebURLLoader
> url_loader(
1630 CreateWebURLLoader(document
, gurl
));
1631 PexeDownloader
* downloader
= new PexeDownloader(instance
,
1638 blink::WebURLRequest url_request
= CreateWebURLRequest(document
, gurl
);
1639 // Mark the request as requesting a PNaCl bitcode file,
1640 // so that component updater can detect this user action.
1641 url_request
.addHTTPHeaderField(
1642 blink::WebString::fromUTF8("Accept"),
1643 blink::WebString::fromUTF8("application/x-pnacl, */*"));
1644 url_request
.setRequestContext(blink::WebURLRequest::RequestContextObject
);
1645 downloader
->Load(url_request
);
1648 const PPB_NaCl_Private nacl_interface
= {
1652 &Are3DInterfacesDisabled
,
1653 &BrokerDuplicateHandle
,
1654 &GetReadExecPnaclFd
,
1655 &CreateTemporaryFile
,
1656 &GetNumberOfProcessors
,
1657 &PPIsNonSFIModeEnabled
,
1658 &ReportTranslationFinished
,
1665 &NaClDebugEnabledForURL
,
1672 &RequestNaClManifest
,
1673 &GetManifestBaseURL
,
1674 &ProcessNaClManifest
,
1675 &DevInterfacesEnabled
,
1676 &ManifestGetProgramURL
,
1677 &GetPNaClResourceInfo
,
1678 &GetCpuFeatureAttrs
,
1680 &ReportSelLdrStatus
,
1689 const PPB_NaCl_Private
* GetNaClPrivateInterface() {
1690 return &nacl_interface
;