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())));
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());
230 callback
.Run(base::File(file_info
.handle
));
233 PP_Instance pp_instance_
;
234 DISALLOW_COPY_AND_ASSIGN(ManifestServiceProxy
);
237 blink::WebURLLoader
* CreateWebURLLoader(const blink::WebDocument
& document
,
239 blink::WebURLLoaderOptions options
;
240 options
.untrustedHTTP
= true;
242 // Options settings here follow the original behavior in the trusted
243 // plugin and PepperURLLoaderHost.
244 if (document
.securityOrigin().canRequest(gurl
)) {
245 options
.allowCredentials
= true;
248 options
.crossOriginRequestPolicy
=
249 blink::WebURLLoaderOptions::CrossOriginRequestPolicyUseAccessControl
;
251 return document
.frame()->createAssociatedURLLoader(options
);
254 blink::WebURLRequest
CreateWebURLRequest(const blink::WebDocument
& document
,
256 blink::WebURLRequest request
;
257 request
.initialize();
258 request
.setURL(gurl
);
259 request
.setFirstPartyForCookies(document
.firstPartyForCookies());
263 int32_t FileDownloaderToPepperError(FileDownloader::Status status
) {
265 case FileDownloader::SUCCESS
:
267 case FileDownloader::ACCESS_DENIED
:
268 return PP_ERROR_NOACCESS
;
269 case FileDownloader::FAILED
:
270 return PP_ERROR_FAILED
;
271 // No default case, to catch unhandled Status values.
273 return PP_ERROR_FAILED
;
276 // Launch NaCl's sel_ldr process.
277 void LaunchSelLdr(PP_Instance instance
,
278 PP_Bool main_service_runtime
,
279 const char* alleged_url
,
280 const PP_NaClFileInfo
* nexe_file_info
,
283 PP_Bool uses_nonsfi_mode
,
284 PP_Bool enable_ppapi_dev
,
285 PP_Bool enable_dyncode_syscalls
,
286 PP_Bool enable_exception_handling
,
287 PP_Bool enable_crash_throttling
,
289 PP_CompletionCallback callback
) {
290 CHECK(ppapi::PpapiGlobals::Get()->GetMainThreadMessageLoop()->
291 BelongsToCurrentThread());
293 // Create the manifest service proxy here, so on error case, it will be
294 // destructed (without passing it to ManifestServiceChannel).
295 scoped_ptr
<ManifestServiceChannel::Delegate
> manifest_service_proxy(
296 new ManifestServiceProxy(instance
));
298 FileDescriptor result_socket
;
299 IPC::Sender
* sender
= content::RenderThread::Get();
302 // If the nexe uses ppapi APIs, we need a routing ID.
303 // To get the routing ID, we must be on the main thread.
304 // Some nexes do not use ppapi and launch from the background thread,
305 // so those nexes can skip finding a routing_id.
307 routing_id
= GetRoutingID(instance
);
309 if (nexe_file_info
->handle
!= PP_kInvalidFileHandle
) {
310 base::File
closer(nexe_file_info
->handle
);
312 ppapi::PpapiGlobals::Get()->GetMainThreadMessageLoop()->PostTask(
314 base::Bind(callback
.func
, callback
.user_data
,
315 static_cast<int32_t>(PP_ERROR_FAILED
)));
320 InstanceInfo instance_info
;
321 instance_info
.url
= GURL(alleged_url
);
323 uint32_t perm_bits
= ppapi::PERMISSION_NONE
;
324 // Conditionally block 'Dev' interfaces. We do this for the NaCl process, so
325 // it's clearer to developers when they are using 'Dev' inappropriately. We
326 // must also check on the trusted side of the proxy.
327 if (enable_ppapi_dev
)
328 perm_bits
|= ppapi::PERMISSION_DEV
;
329 instance_info
.permissions
=
330 ppapi::PpapiPermissions::GetForCommandLine(perm_bits
);
331 std::string error_message_string
;
332 NaClLaunchResult launch_result
;
334 IPC::PlatformFileForTransit nexe_for_transit
=
335 IPC::InvalidPlatformFileForTransit();
336 #if defined(OS_POSIX)
337 if (nexe_file_info
->handle
!= PP_kInvalidFileHandle
)
338 nexe_for_transit
= base::FileDescriptor(nexe_file_info
->handle
, true);
339 #elif defined(OS_WIN)
340 // Duplicate the handle on the browser side instead of the renderer.
341 // This is because BrokerGetFileForProcess isn't part of content/public, and
342 // it's simpler to do the duplication in the browser anyway.
343 nexe_for_transit
= nexe_file_info
->handle
;
345 #error Unsupported target platform.
347 if (!sender
->Send(new NaClHostMsg_LaunchNaCl(
349 instance_info
.url
.spec(),
351 nexe_file_info
->token_lo
,
352 nexe_file_info
->token_hi
,
356 PP_ToBool(uses_nonsfi_mode
),
357 PP_ToBool(enable_dyncode_syscalls
),
358 PP_ToBool(enable_exception_handling
),
359 PP_ToBool(enable_crash_throttling
)),
361 &error_message_string
))) {
362 ppapi::PpapiGlobals::Get()->GetMainThreadMessageLoop()->PostTask(
364 base::Bind(callback
.func
, callback
.user_data
,
365 static_cast<int32_t>(PP_ERROR_FAILED
)));
369 if (!error_message_string
.empty()) {
370 if (PP_ToBool(main_service_runtime
)) {
371 NexeLoadManager
* load_manager
= NexeLoadManager::Get(instance
);
373 load_manager
->ReportLoadError(PP_NACL_ERROR_SEL_LDR_LAUNCH
,
374 "ServiceRuntime: failed to start",
375 error_message_string
);
378 ppapi::PpapiGlobals::Get()->GetMainThreadMessageLoop()->PostTask(
380 base::Bind(callback
.func
, callback
.user_data
,
381 static_cast<int32_t>(PP_ERROR_FAILED
)));
384 result_socket
= launch_result
.imc_channel_handle
;
385 instance_info
.channel_handle
= launch_result
.ppapi_ipc_channel_handle
;
386 instance_info
.plugin_pid
= launch_result
.plugin_pid
;
387 instance_info
.plugin_child_id
= launch_result
.plugin_child_id
;
389 // Don't save instance_info if channel handle is invalid.
390 if (IsValidChannelHandle(instance_info
.channel_handle
))
391 g_instance_info
.Get()[instance
] = instance_info
;
393 *(static_cast<NaClHandle
*>(imc_handle
)) = ToNativeHandle(result_socket
);
395 NexeLoadManager
* load_manager
= NexeLoadManager::Get(instance
);
396 DCHECK(load_manager
);
398 PostPPCompletionCallback(callback
, PP_ERROR_FAILED
);
399 base::SharedMemory::CloseHandle(launch_result
.crash_info_shmem_handle
);
403 // Store the crash information shared memory handle.
404 load_manager
->set_crash_info_shmem_handle(
405 launch_result
.crash_info_shmem_handle
);
407 // Create the trusted plugin channel.
408 if (IsValidChannelHandle(launch_result
.trusted_ipc_channel_handle
)) {
409 bool report_exit_status
= PP_ToBool(main_service_runtime
);
410 scoped_ptr
<TrustedPluginChannel
> trusted_plugin_channel(
411 new TrustedPluginChannel(
413 launch_result
.trusted_ipc_channel_handle
,
414 content::RenderThread::Get()->GetShutdownEvent(),
415 report_exit_status
));
416 load_manager
->set_trusted_plugin_channel(trusted_plugin_channel
.Pass());
418 PostPPCompletionCallback(callback
, PP_ERROR_FAILED
);
422 // Create the manifest service handle as well.
423 // For security hardening, disable the IPCs for open_resource() when they
424 // aren't needed. PNaCl doesn't expose open_resource(), and the new
425 // open_resource() IPCs are currently only used for Non-SFI NaCl so far,
426 // not SFI NaCl. Note that enable_dyncode_syscalls is true if and only if
427 // the plugin is a non-PNaCl plugin.
429 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 NexeDidCrash(PP_Instance instance
) {
753 NexeLoadManager
* load_manager
= NexeLoadManager::Get(instance
);
755 load_manager
->NexeDidCrash();
758 void InstanceCreated(PP_Instance instance
) {
759 NexeLoadManager::Create(instance
);
762 void InstanceDestroyed(PP_Instance instance
) {
763 DeleteJsonManifest(instance
);
764 NexeLoadManager::Delete(instance
);
767 PP_Bool
NaClDebugEnabledForURL(const char* alleged_nmf_url
) {
768 if (!CommandLine::ForCurrentProcess()->HasSwitch(switches::kEnableNaClDebug
))
770 IPC::Sender
* sender
= content::RenderThread::Get();
772 bool should_debug
= false;
774 sender
->Send(new NaClHostMsg_NaClDebugEnabledForURL(GURL(alleged_nmf_url
),
779 void LogToConsole(PP_Instance instance
, const char* message
) {
780 NexeLoadManager
* load_manager
= NexeLoadManager::Get(instance
);
781 DCHECK(load_manager
);
783 load_manager
->LogToConsole(std::string(message
));
786 PP_NaClReadyState
GetNaClReadyState(PP_Instance instance
) {
787 NexeLoadManager
* load_manager
= NexeLoadManager::Get(instance
);
788 DCHECK(load_manager
);
790 return load_manager
->nacl_ready_state();
791 return PP_NACL_READY_STATE_UNSENT
;
794 void Vlog(const char* message
) {
798 void InitializePlugin(PP_Instance instance
,
801 const char* argv
[]) {
802 NexeLoadManager
* load_manager
= NexeLoadManager::Get(instance
);
803 DCHECK(load_manager
);
805 load_manager
->InitializePlugin(argc
, argn
, argv
);
808 int64_t GetNexeSize(PP_Instance instance
) {
809 NexeLoadManager
* load_manager
= NexeLoadManager::Get(instance
);
810 DCHECK(load_manager
);
812 return load_manager
->nexe_size();
816 void DownloadManifestToBuffer(PP_Instance instance
,
817 struct PP_CompletionCallback callback
);
819 bool CreateJsonManifest(PP_Instance instance
,
820 const std::string
& manifest_url
,
821 const std::string
& manifest_data
);
823 void RequestNaClManifest(PP_Instance instance
,
824 PP_CompletionCallback callback
) {
825 NexeLoadManager
* load_manager
= NexeLoadManager::Get(instance
);
826 DCHECK(load_manager
);
828 ppapi::PpapiGlobals::Get()->GetMainThreadMessageLoop()->PostTask(
830 base::Bind(callback
.func
, callback
.user_data
,
831 static_cast<int32_t>(PP_ERROR_FAILED
)));
835 std::string url
= load_manager
->GetManifestURLArgument();
836 if (url
.empty() || !load_manager
->RequestNaClManifest(url
)) {
837 ppapi::PpapiGlobals::Get()->GetMainThreadMessageLoop()->PostTask(
839 base::Bind(callback
.func
, callback
.user_data
,
840 static_cast<int32_t>(PP_ERROR_FAILED
)));
844 const GURL
& base_url
= load_manager
->manifest_base_url();
845 if (base_url
.SchemeIs("data")) {
847 std::string mime_type
;
850 int32_t error
= PP_ERROR_FAILED
;
851 if (net::DataURL::Parse(gurl
, &mime_type
, &charset
, &data
)) {
852 if (data
.size() <= ManifestDownloader::kNaClManifestMaxFileBytes
) {
853 if (CreateJsonManifest(instance
, base_url
.spec(), data
))
856 load_manager
->ReportLoadError(PP_NACL_ERROR_MANIFEST_TOO_LARGE
,
857 "manifest file too large.");
860 load_manager
->ReportLoadError(PP_NACL_ERROR_MANIFEST_LOAD_URL
,
861 "could not load manifest url.");
863 ppapi::PpapiGlobals::Get()->GetMainThreadMessageLoop()->PostTask(
865 base::Bind(callback
.func
, callback
.user_data
, error
));
867 DownloadManifestToBuffer(instance
, callback
);
871 PP_Var
GetManifestBaseURL(PP_Instance instance
) {
872 NexeLoadManager
* load_manager
= NexeLoadManager::Get(instance
);
873 DCHECK(load_manager
);
875 return PP_MakeUndefined();
876 const GURL
& gurl
= load_manager
->manifest_base_url();
877 if (!gurl
.is_valid())
878 return PP_MakeUndefined();
879 return ppapi::StringVar::StringToPPVar(gurl
.spec());
882 void ProcessNaClManifest(PP_Instance instance
, const char* program_url
) {
883 nacl::NexeLoadManager
* load_manager
= NexeLoadManager::Get(instance
);
885 load_manager
->ProcessNaClManifest(program_url
);
888 PP_Bool
DevInterfacesEnabled(PP_Instance instance
) {
889 nacl::NexeLoadManager
* load_manager
= NexeLoadManager::Get(instance
);
891 return PP_FromBool(load_manager
->DevInterfacesEnabled());
895 void DownloadManifestToBufferCompletion(PP_Instance instance
,
896 struct PP_CompletionCallback callback
,
897 base::Time start_time
,
898 PP_NaClError pp_nacl_error
,
899 const std::string
& data
);
901 void DownloadManifestToBuffer(PP_Instance instance
,
902 struct PP_CompletionCallback callback
) {
903 nacl::NexeLoadManager
* load_manager
= NexeLoadManager::Get(instance
);
904 DCHECK(load_manager
);
905 content::PepperPluginInstance
* plugin_instance
=
906 content::PepperPluginInstance::Get(instance
);
907 if (!load_manager
|| !plugin_instance
) {
908 ppapi::PpapiGlobals::Get()->GetMainThreadMessageLoop()->PostTask(
910 base::Bind(callback
.func
, callback
.user_data
,
911 static_cast<int32_t>(PP_ERROR_FAILED
)));
913 const blink::WebDocument
& document
=
914 plugin_instance
->GetContainer()->element().document();
916 const GURL
& gurl
= load_manager
->manifest_base_url();
917 scoped_ptr
<blink::WebURLLoader
> url_loader(
918 CreateWebURLLoader(document
, gurl
));
919 blink::WebURLRequest request
= CreateWebURLRequest(document
, gurl
);
921 // ManifestDownloader deletes itself after invoking the callback.
922 ManifestDownloader
* manifest_downloader
= new ManifestDownloader(
924 load_manager
->is_installed(),
925 base::Bind(DownloadManifestToBufferCompletion
,
926 instance
, callback
, base::Time::Now()));
927 manifest_downloader
->Load(request
);
930 void DownloadManifestToBufferCompletion(PP_Instance instance
,
931 struct PP_CompletionCallback callback
,
932 base::Time start_time
,
933 PP_NaClError pp_nacl_error
,
934 const std::string
& data
) {
935 base::TimeDelta download_time
= base::Time::Now() - start_time
;
936 HistogramTimeSmall("NaCl.Perf.StartupTime.ManifestDownload",
937 download_time
.InMilliseconds());
939 nacl::NexeLoadManager
* load_manager
= NexeLoadManager::Get(instance
);
941 callback
.func(callback
.user_data
, PP_ERROR_ABORTED
);
946 switch (pp_nacl_error
) {
947 case PP_NACL_ERROR_LOAD_SUCCESS
:
950 case PP_NACL_ERROR_MANIFEST_LOAD_URL
:
951 pp_error
= PP_ERROR_FAILED
;
952 load_manager
->ReportLoadError(PP_NACL_ERROR_MANIFEST_LOAD_URL
,
953 "could not load manifest url.");
955 case PP_NACL_ERROR_MANIFEST_TOO_LARGE
:
956 pp_error
= PP_ERROR_FILETOOBIG
;
957 load_manager
->ReportLoadError(PP_NACL_ERROR_MANIFEST_TOO_LARGE
,
958 "manifest file too large.");
960 case PP_NACL_ERROR_MANIFEST_NOACCESS_URL
:
961 pp_error
= PP_ERROR_NOACCESS
;
962 load_manager
->ReportLoadError(PP_NACL_ERROR_MANIFEST_NOACCESS_URL
,
963 "access to manifest url was denied.");
967 pp_error
= PP_ERROR_FAILED
;
968 load_manager
->ReportLoadError(PP_NACL_ERROR_MANIFEST_LOAD_URL
,
969 "could not load manifest url.");
972 if (pp_error
== PP_OK
) {
973 std::string base_url
= load_manager
->manifest_base_url().spec();
974 if (!CreateJsonManifest(instance
, base_url
, data
))
975 pp_error
= PP_ERROR_FAILED
;
977 callback
.func(callback
.user_data
, pp_error
);
980 bool CreateJsonManifest(PP_Instance instance
,
981 const std::string
& manifest_url
,
982 const std::string
& manifest_data
) {
983 HistogramSizeKB("NaCl.Perf.Size.Manifest",
984 static_cast<int32_t>(manifest_data
.length() / 1024));
986 nacl::NexeLoadManager
* load_manager
= NexeLoadManager::Get(instance
);
990 const char* isa_type
;
991 if (load_manager
->IsPNaCl())
992 isa_type
= kPortableArch
;
994 isa_type
= GetSandboxArch();
996 scoped_ptr
<nacl::JsonManifest
> j(
997 new nacl::JsonManifest(
998 manifest_url
.c_str(),
1000 IsNonSFIModeEnabled(),
1001 PP_ToBool(NaClDebugEnabledForURL(manifest_url
.c_str()))));
1002 JsonManifest::ErrorInfo error_info
;
1003 if (j
->Init(manifest_data
.c_str(), &error_info
)) {
1004 AddJsonManifest(instance
, j
.Pass());
1007 load_manager
->ReportLoadError(error_info
.error
, error_info
.string
);
1011 PP_Bool
ManifestGetProgramURL(PP_Instance instance
,
1012 PP_Var
* pp_full_url
,
1013 PP_PNaClOptions
* pnacl_options
,
1014 PP_Bool
* pp_uses_nonsfi_mode
) {
1015 nacl::NexeLoadManager
* load_manager
= NexeLoadManager::Get(instance
);
1017 JsonManifest
* manifest
= GetJsonManifest(instance
);
1018 if (manifest
== NULL
)
1021 bool uses_nonsfi_mode
;
1022 std::string full_url
;
1023 JsonManifest::ErrorInfo error_info
;
1024 if (manifest
->GetProgramURL(&full_url
, pnacl_options
, &uses_nonsfi_mode
,
1026 *pp_full_url
= ppapi::StringVar::StringToPPVar(full_url
);
1027 *pp_uses_nonsfi_mode
= PP_FromBool(uses_nonsfi_mode
);
1032 load_manager
->ReportLoadError(error_info
.error
, error_info
.string
);
1036 bool ManifestResolveKey(PP_Instance instance
,
1037 bool is_helper_process
,
1038 const std::string
& key
,
1039 std::string
* full_url
,
1040 PP_PNaClOptions
* pnacl_options
) {
1041 // For "helper" processes (llc and ld, for PNaCl translation), we resolve
1042 // keys manually as there is no existing .nmf file to parse.
1043 if (is_helper_process
) {
1044 pnacl_options
->translate
= PP_FALSE
;
1045 // We can only resolve keys in the files/ namespace.
1046 const std::string kFilesPrefix
= "files/";
1047 if (key
.find(kFilesPrefix
) == std::string::npos
) {
1048 nacl::NexeLoadManager
* load_manager
= NexeLoadManager::Get(instance
);
1050 load_manager
->ReportLoadError(PP_NACL_ERROR_MANIFEST_RESOLVE_URL
,
1051 "key did not start with files/");
1054 std::string key_basename
= key
.substr(kFilesPrefix
.length());
1055 *full_url
= std::string(kPNaClTranslatorBaseUrl
) + GetSandboxArch() + "/" +
1060 JsonManifest
* manifest
= GetJsonManifest(instance
);
1061 if (manifest
== NULL
)
1064 return manifest
->ResolveKey(key
, full_url
, pnacl_options
);
1067 PP_Bool
GetPNaClResourceInfo(PP_Instance instance
,
1068 PP_Var
* llc_tool_name
,
1069 PP_Var
* ld_tool_name
) {
1070 static const char kFilename
[] = "chrome://pnacl-translator/pnacl.json";
1071 NexeLoadManager
* load_manager
= NexeLoadManager::Get(instance
);
1072 DCHECK(load_manager
);
1076 uint64_t nonce_lo
= 0;
1077 uint64_t nonce_hi
= 0;
1078 base::File
file(GetReadonlyPnaclFd(kFilename
, false /* is_executable */,
1079 &nonce_lo
, &nonce_hi
));
1080 if (!file
.IsValid()) {
1081 load_manager
->ReportLoadError(
1082 PP_NACL_ERROR_PNACL_RESOURCE_FETCH
,
1083 "The Portable Native Client (pnacl) component is not "
1084 "installed. Please consult chrome://components for more "
1089 base::File::Info file_info
;
1090 if (!file
.GetInfo(&file_info
)) {
1091 load_manager
->ReportLoadError(
1092 PP_NACL_ERROR_PNACL_RESOURCE_FETCH
,
1093 std::string("GetPNaClResourceInfo, GetFileInfo failed for: ") +
1098 if (file_info
.size
> 1 << 20) {
1099 load_manager
->ReportLoadError(
1100 PP_NACL_ERROR_PNACL_RESOURCE_FETCH
,
1101 std::string("GetPNaClResourceInfo, file too large: ") + kFilename
);
1105 scoped_ptr
<char[]> buffer(new char[file_info
.size
+ 1]);
1106 if (buffer
.get() == NULL
) {
1107 load_manager
->ReportLoadError(
1108 PP_NACL_ERROR_PNACL_RESOURCE_FETCH
,
1109 std::string("GetPNaClResourceInfo, couldn't allocate for: ") +
1114 int rc
= file
.Read(0, buffer
.get(), file_info
.size
);
1116 load_manager
->ReportLoadError(
1117 PP_NACL_ERROR_PNACL_RESOURCE_FETCH
,
1118 std::string("GetPNaClResourceInfo, reading failed for: ") + kFilename
);
1122 // Null-terminate the bytes we we read from the file.
1123 buffer
.get()[rc
] = 0;
1125 // Expect the JSON file to contain a top-level object (dictionary).
1126 Json::Reader json_reader
;
1127 Json::Value json_data
;
1128 if (!json_reader
.parse(buffer
.get(), json_data
)) {
1129 load_manager
->ReportLoadError(
1130 PP_NACL_ERROR_PNACL_RESOURCE_FETCH
,
1131 std::string("Parsing resource info failed: JSON parse error: ") +
1132 json_reader
.getFormattedErrorMessages());
1136 if (!json_data
.isObject()) {
1137 load_manager
->ReportLoadError(
1138 PP_NACL_ERROR_PNACL_RESOURCE_FETCH
,
1139 "Parsing resource info failed: Malformed JSON dictionary");
1143 if (json_data
.isMember("pnacl-llc-name")) {
1144 Json::Value json_name
= json_data
["pnacl-llc-name"];
1145 if (json_name
.isString()) {
1146 std::string llc_tool_name_str
= json_name
.asString();
1147 *llc_tool_name
= ppapi::StringVar::StringToPPVar(llc_tool_name_str
);
1151 if (json_data
.isMember("pnacl-ld-name")) {
1152 Json::Value json_name
= json_data
["pnacl-ld-name"];
1153 if (json_name
.isString()) {
1154 std::string ld_tool_name_str
= json_name
.asString();
1155 *ld_tool_name
= ppapi::StringVar::StringToPPVar(ld_tool_name_str
);
1161 PP_Var
GetCpuFeatureAttrs() {
1162 return ppapi::StringVar::StringToPPVar(GetCpuFeatures());
1165 void PostMessageToJavaScriptMainThread(PP_Instance instance
,
1166 const std::string
& message
) {
1167 content::PepperPluginInstance
* plugin_instance
=
1168 content::PepperPluginInstance::Get(instance
);
1169 if (plugin_instance
) {
1170 PP_Var message_var
= ppapi::StringVar::StringToPPVar(message
);
1171 plugin_instance
->PostMessageToJavaScript(message_var
);
1172 ppapi::PpapiGlobals::Get()->GetVarTracker()->ReleaseVar(message_var
);
1176 void PostMessageToJavaScript(PP_Instance instance
, const char* message
) {
1177 ppapi::PpapiGlobals::Get()->GetMainThreadMessageLoop()->PostTask(
1179 base::Bind(&PostMessageToJavaScriptMainThread
,
1181 std::string(message
)));
1184 // Encapsulates some of the state for a call to DownloadNexe to prevent
1185 // argument lists from getting too long.
1186 struct DownloadNexeRequest
{
1187 PP_Instance instance
;
1189 PP_CompletionCallback callback
;
1190 base::Time start_time
;
1193 // A utility class to ensure that we don't send progress events more often than
1194 // every 10ms for a given file.
1195 class ProgressEventRateLimiter
{
1197 explicit ProgressEventRateLimiter(PP_Instance instance
)
1198 : instance_(instance
) { }
1200 void ReportProgress(const std::string
& url
,
1201 int64_t total_bytes_received
,
1202 int64_t total_bytes_to_be_received
) {
1203 base::Time now
= base::Time::Now();
1204 if (now
- last_event_
> base::TimeDelta::FromMilliseconds(10)) {
1205 DispatchProgressEvent(instance_
,
1206 ProgressEvent(PP_NACL_EVENT_PROGRESS
,
1208 total_bytes_to_be_received
>= 0,
1209 total_bytes_received
,
1210 total_bytes_to_be_received
));
1216 PP_Instance instance_
;
1217 base::Time last_event_
;
1220 void DownloadNexeCompletion(const DownloadNexeRequest
& request
,
1221 PP_NaClFileInfo
* out_file_info
,
1222 FileDownloader::Status status
,
1223 base::File target_file
,
1226 void DownloadNexe(PP_Instance instance
,
1228 PP_NaClFileInfo
* out_file_info
,
1229 PP_CompletionCallback callback
) {
1231 CHECK(out_file_info
);
1232 DownloadNexeRequest request
;
1233 request
.instance
= instance
;
1235 request
.callback
= callback
;
1236 request
.start_time
= base::Time::Now();
1238 // Try the fast path for retrieving the file first.
1239 PP_FileHandle handle
= OpenNaClExecutable(instance
,
1241 &out_file_info
->token_lo
,
1242 &out_file_info
->token_hi
);
1243 if (handle
!= PP_kInvalidFileHandle
) {
1244 DownloadNexeCompletion(request
,
1246 FileDownloader::SUCCESS
,
1252 // The fast path didn't work, we'll fetch the file using URLLoader and write
1253 // it to local storage.
1254 base::File
target_file(CreateTemporaryFile(instance
));
1257 content::PepperPluginInstance
* plugin_instance
=
1258 content::PepperPluginInstance::Get(instance
);
1259 if (!plugin_instance
) {
1260 ppapi::PpapiGlobals::Get()->GetMainThreadMessageLoop()->PostTask(
1262 base::Bind(callback
.func
, callback
.user_data
,
1263 static_cast<int32_t>(PP_ERROR_FAILED
)));
1265 const blink::WebDocument
& document
=
1266 plugin_instance
->GetContainer()->element().document();
1267 scoped_ptr
<blink::WebURLLoader
> url_loader(
1268 CreateWebURLLoader(document
, gurl
));
1269 blink::WebURLRequest url_request
= CreateWebURLRequest(document
, gurl
);
1271 ProgressEventRateLimiter
* tracker
= new ProgressEventRateLimiter(instance
);
1273 // FileDownloader deletes itself after invoking DownloadNexeCompletion.
1274 FileDownloader
* file_downloader
= new FileDownloader(
1277 base::Bind(&DownloadNexeCompletion
, request
, out_file_info
),
1278 base::Bind(&ProgressEventRateLimiter::ReportProgress
,
1279 base::Owned(tracker
), std::string(url
)));
1280 file_downloader
->Load(url_request
);
1283 void DownloadNexeCompletion(const DownloadNexeRequest
& request
,
1284 PP_NaClFileInfo
* out_file_info
,
1285 FileDownloader::Status status
,
1286 base::File target_file
,
1288 int32_t pp_error
= FileDownloaderToPepperError(status
);
1289 int64_t bytes_read
= -1;
1290 if (pp_error
== PP_OK
&& target_file
.IsValid()) {
1291 base::File::Info info
;
1292 if (target_file
.GetInfo(&info
))
1293 bytes_read
= info
.size
;
1296 if (bytes_read
== -1) {
1297 target_file
.Close();
1298 pp_error
= PP_ERROR_FAILED
;
1301 base::TimeDelta download_time
= base::Time::Now() - request
.start_time
;
1303 NexeLoadManager
* load_manager
= NexeLoadManager::Get(request
.instance
);
1305 load_manager
->NexeFileDidOpen(pp_error
,
1313 if (pp_error
== PP_OK
&& target_file
.IsValid())
1314 out_file_info
->handle
= target_file
.TakePlatformFile();
1316 out_file_info
->handle
= PP_kInvalidFileHandle
;
1318 request
.callback
.func(request
.callback
.user_data
, pp_error
);
1321 void DownloadFileCompletion(
1322 const DownloadFileCallback
& callback
,
1323 FileDownloader::Status status
,
1326 int32_t pp_error
= FileDownloaderToPepperError(status
);
1327 PP_NaClFileInfo file_info
;
1328 if (pp_error
== PP_OK
) {
1329 file_info
.handle
= file
.TakePlatformFile();
1330 file_info
.token_lo
= 0;
1331 file_info
.token_hi
= 0;
1333 file_info
= kInvalidNaClFileInfo
;
1336 callback
.Run(pp_error
, file_info
);
1339 void DownloadFile(PP_Instance instance
,
1340 const std::string
& url
,
1341 const DownloadFileCallback
& callback
) {
1342 DCHECK(ppapi::PpapiGlobals::Get()->GetMainThreadMessageLoop()->
1343 BelongsToCurrentThread());
1345 NexeLoadManager
* load_manager
= NexeLoadManager::Get(instance
);
1346 DCHECK(load_manager
);
1347 if (!load_manager
) {
1348 base::MessageLoop::current()->PostTask(
1350 base::Bind(callback
,
1351 static_cast<int32_t>(PP_ERROR_FAILED
),
1352 kInvalidNaClFileInfo
));
1356 // Handle special PNaCl support files which are installed on the user's
1358 if (url
.find(kPNaClTranslatorBaseUrl
, 0) == 0) {
1359 PP_NaClFileInfo file_info
= kInvalidNaClFileInfo
;
1360 PP_FileHandle handle
= GetReadonlyPnaclFd(url
.c_str(),
1361 false /* is_executable */,
1362 &file_info
.token_lo
,
1363 &file_info
.token_hi
);
1364 if (handle
== PP_kInvalidFileHandle
) {
1365 base::MessageLoop::current()->PostTask(
1367 base::Bind(callback
,
1368 static_cast<int32_t>(PP_ERROR_FAILED
),
1369 kInvalidNaClFileInfo
));
1372 file_info
.handle
= handle
;
1373 base::MessageLoop::current()->PostTask(
1375 base::Bind(callback
, static_cast<int32_t>(PP_OK
), file_info
));
1379 // We have to ensure that this url resolves relative to the plugin base url
1380 // before downloading it.
1381 const GURL
& test_gurl
= load_manager
->plugin_base_url().Resolve(url
);
1382 if (!test_gurl
.is_valid()) {
1383 base::MessageLoop::current()->PostTask(
1385 base::Bind(callback
,
1386 static_cast<int32_t>(PP_ERROR_FAILED
),
1387 kInvalidNaClFileInfo
));
1391 // Try the fast path for retrieving the file first.
1392 uint64_t file_token_lo
= 0;
1393 uint64_t file_token_hi
= 0;
1394 PP_FileHandle file_handle
= OpenNaClExecutable(instance
,
1398 if (file_handle
!= PP_kInvalidFileHandle
) {
1399 PP_NaClFileInfo file_info
;
1400 file_info
.handle
= file_handle
;
1401 file_info
.token_lo
= file_token_lo
;
1402 file_info
.token_hi
= file_token_hi
;
1403 base::MessageLoop::current()->PostTask(
1405 base::Bind(callback
, static_cast<int32_t>(PP_OK
), file_info
));
1409 // The fast path didn't work, we'll fetch the file using URLLoader and write
1410 // it to local storage.
1411 base::File
target_file(CreateTemporaryFile(instance
));
1414 content::PepperPluginInstance
* plugin_instance
=
1415 content::PepperPluginInstance::Get(instance
);
1416 if (!plugin_instance
) {
1417 base::MessageLoop::current()->PostTask(
1419 base::Bind(callback
,
1420 static_cast<int32_t>(PP_ERROR_FAILED
),
1421 kInvalidNaClFileInfo
));
1423 const blink::WebDocument
& document
=
1424 plugin_instance
->GetContainer()->element().document();
1425 scoped_ptr
<blink::WebURLLoader
> url_loader(
1426 CreateWebURLLoader(document
, gurl
));
1427 blink::WebURLRequest url_request
= CreateWebURLRequest(document
, gurl
);
1429 ProgressEventRateLimiter
* tracker
= new ProgressEventRateLimiter(instance
);
1431 // FileDownloader deletes itself after invoking DownloadNexeCompletion.
1432 FileDownloader
* file_downloader
= new FileDownloader(
1435 base::Bind(&DownloadFileCompletion
, callback
),
1436 base::Bind(&ProgressEventRateLimiter::ReportProgress
,
1437 base::Owned(tracker
), std::string(url
)));
1438 file_downloader
->Load(url_request
);
1441 void ReportSelLdrStatus(PP_Instance instance
,
1442 int32_t load_status
,
1443 int32_t max_status
) {
1444 HistogramEnumerate("NaCl.LoadStatus.SelLdr", load_status
, max_status
);
1445 NexeLoadManager
* load_manager
= NexeLoadManager::Get(instance
);
1446 DCHECK(load_manager
);
1450 // Gather data to see if being installed changes load outcomes.
1451 const char* name
= load_manager
->is_installed() ?
1452 "NaCl.LoadStatus.SelLdr.InstalledApp" :
1453 "NaCl.LoadStatus.SelLdr.NotInstalledApp";
1454 HistogramEnumerate(name
, load_status
, max_status
);
1457 void LogTranslateTime(const char* histogram_name
,
1458 int64_t time_in_us
) {
1459 ppapi::PpapiGlobals::Get()->GetMainThreadMessageLoop()->PostTask(
1461 base::Bind(&HistogramTimeTranslation
,
1462 std::string(histogram_name
),
1463 time_in_us
/ 1000));
1466 void DidOpenManifestEntry(PP_NaClFileInfo
* out_file_info
,
1467 PP_CompletionCallback callback
,
1469 const PP_NaClFileInfo
& file_info
) {
1470 if (pp_error
== PP_OK
)
1471 *out_file_info
= file_info
;
1472 callback
.func(callback
.user_data
, pp_error
);
1475 void OpenManifestEntry(PP_Instance instance
,
1476 PP_Bool is_helper_process
,
1478 PP_NaClFileInfo
* out_file_info
,
1479 PP_CompletionCallback callback
) {
1481 PP_PNaClOptions pnacl_options
;
1482 pnacl_options
.translate
= PP_FALSE
;
1483 pnacl_options
.is_debug
= PP_FALSE
;
1484 pnacl_options
.opt_level
= 2;
1485 if (!ManifestResolveKey(instance
,
1486 PP_ToBool(is_helper_process
),
1490 PostPPCompletionCallback(callback
, PP_ERROR_FAILED
);
1493 // TODO(teravest): Make a type like PP_NaClFileInfo to use for DownloadFile
1494 // that would close the file handle on destruction.
1495 DownloadFile(instance
, url
,
1496 base::Bind(&DidOpenManifestEntry
, out_file_info
, callback
));
1499 void SetPNaClStartTime(PP_Instance instance
) {
1500 NexeLoadManager
* load_manager
= NexeLoadManager::Get(instance
);
1502 load_manager
->set_pnacl_start_time(base::Time::Now());
1505 // PexeDownloader is responsible for deleting itself when the download
1507 class PexeDownloader
: public blink::WebURLLoaderClient
{
1509 PexeDownloader(PP_Instance instance
,
1510 scoped_ptr
<blink::WebURLLoader
> url_loader
,
1511 const std::string
& pexe_url
,
1512 int32_t pexe_opt_level
,
1513 const PPP_PexeStreamHandler
* stream_handler
,
1514 void* stream_handler_user_data
)
1515 : instance_(instance
),
1516 url_loader_(url_loader
.Pass()),
1517 pexe_url_(pexe_url
),
1518 pexe_opt_level_(pexe_opt_level
),
1519 stream_handler_(stream_handler
),
1520 stream_handler_user_data_(stream_handler_user_data
),
1522 expected_content_length_(-1),
1523 weak_factory_(this) { }
1525 void Load(const blink::WebURLRequest
& request
) {
1526 url_loader_
->loadAsynchronously(request
, this);
1530 virtual void didReceiveResponse(blink::WebURLLoader
* loader
,
1531 const blink::WebURLResponse
& response
) {
1532 success_
= (response
.httpStatusCode() == 200);
1536 expected_content_length_
= response
.expectedContentLength();
1538 // Defer loading after receiving headers. This is because we may already
1539 // have a cached translated nexe, so check for that now.
1540 url_loader_
->setDefersLoading(true);
1542 std::string etag
= response
.httpHeaderField("etag").utf8();
1543 std::string last_modified
=
1544 response
.httpHeaderField("last-modified").utf8();
1545 base::Time last_modified_time
;
1546 base::Time::FromString(last_modified
.c_str(), &last_modified_time
);
1548 bool has_no_store_header
= false;
1549 std::string cache_control
=
1550 response
.httpHeaderField("cache-control").utf8();
1552 std::vector
<std::string
> values
;
1553 base::SplitString(cache_control
, ',', &values
);
1554 for (std::vector
<std::string
>::const_iterator it
= values
.begin();
1557 if (base::StringToLowerASCII(*it
) == "no-store")
1558 has_no_store_header
= true;
1561 GetNexeFd(instance_
,
1566 has_no_store_header
,
1567 base::Bind(&PexeDownloader::didGetNexeFd
,
1568 weak_factory_
.GetWeakPtr()));
1571 virtual void didGetNexeFd(int32_t pp_error
,
1573 PP_FileHandle file_handle
) {
1574 if (!content::PepperPluginInstance::Get(instance_
)) {
1579 HistogramEnumerate("NaCl.Perf.PNaClCache.IsHit", cache_hit
, 2);
1581 stream_handler_
->DidCacheHit(stream_handler_user_data_
, file_handle
);
1583 // We delete the PexeDownloader at this point since we successfully got a
1584 // cached, translated nexe.
1588 stream_handler_
->DidCacheMiss(stream_handler_user_data_
,
1589 expected_content_length_
,
1592 // No translated nexe was found in the cache, so we should download the
1593 // file to start streaming it.
1594 url_loader_
->setDefersLoading(false);
1597 virtual void didReceiveData(blink::WebURLLoader
* loader
,
1600 int encoded_data_length
) {
1601 if (content::PepperPluginInstance::Get(instance_
)) {
1602 // Stream the data we received to the stream callback.
1603 stream_handler_
->DidStreamData(stream_handler_user_data_
,
1609 virtual void didFinishLoading(blink::WebURLLoader
* loader
,
1611 int64_t total_encoded_data_length
) {
1612 int32_t result
= success_
? PP_OK
: PP_ERROR_FAILED
;
1614 if (content::PepperPluginInstance::Get(instance_
))
1615 stream_handler_
->DidFinishStream(stream_handler_user_data_
, result
);
1619 virtual void didFail(blink::WebURLLoader
* loader
,
1620 const blink::WebURLError
& error
) {
1624 PP_Instance instance_
;
1625 scoped_ptr
<blink::WebURLLoader
> url_loader_
;
1626 std::string pexe_url_
;
1627 int32_t pexe_opt_level_
;
1628 const PPP_PexeStreamHandler
* stream_handler_
;
1629 void* stream_handler_user_data_
;
1631 int64_t expected_content_length_
;
1632 base::WeakPtrFactory
<PexeDownloader
> weak_factory_
;
1635 void StreamPexe(PP_Instance instance
,
1636 const char* pexe_url
,
1638 const PPP_PexeStreamHandler
* handler
,
1639 void* handler_user_data
) {
1640 content::PepperPluginInstance
* plugin_instance
=
1641 content::PepperPluginInstance::Get(instance
);
1642 if (!plugin_instance
) {
1643 base::MessageLoop::current()->PostTask(
1645 base::Bind(handler
->DidFinishStream
,
1647 static_cast<int32_t>(PP_ERROR_FAILED
)));
1651 GURL
gurl(pexe_url
);
1652 const blink::WebDocument
& document
=
1653 plugin_instance
->GetContainer()->element().document();
1654 scoped_ptr
<blink::WebURLLoader
> url_loader(
1655 CreateWebURLLoader(document
, gurl
));
1656 PexeDownloader
* downloader
= new PexeDownloader(instance
,
1663 blink::WebURLRequest url_request
= CreateWebURLRequest(document
, gurl
);
1664 // Mark the request as requesting a PNaCl bitcode file,
1665 // so that component updater can detect this user action.
1666 url_request
.addHTTPHeaderField(
1667 blink::WebString::fromUTF8("Accept"),
1668 blink::WebString::fromUTF8("application/x-pnacl, */*"));
1669 downloader
->Load(url_request
);
1672 const PPB_NaCl_Private nacl_interface
= {
1676 &Are3DInterfacesDisabled
,
1677 &BrokerDuplicateHandle
,
1678 &GetReadExecPnaclFd
,
1679 &CreateTemporaryFile
,
1680 &GetNumberOfProcessors
,
1681 &PPIsNonSFIModeEnabled
,
1682 &ReportTranslationFinished
,
1690 &NaClDebugEnabledForURL
,
1697 &RequestNaClManifest
,
1698 &GetManifestBaseURL
,
1699 &ProcessNaClManifest
,
1700 &DevInterfacesEnabled
,
1701 &ManifestGetProgramURL
,
1702 &GetPNaClResourceInfo
,
1703 &GetCpuFeatureAttrs
,
1704 &PostMessageToJavaScript
,
1706 &ReportSelLdrStatus
,
1715 const PPB_NaCl_Private
* GetNaClPrivateInterface() {
1716 return &nacl_interface
;