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 ~ManifestServiceProxy() override
{}
170 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
);
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 NaClAppProcessType
PP_ToNaClAppProcessType(
279 PP_NaClAppProcessType pp_process_type
) {
280 #define STATICALLY_CHECK_NACLAPPPROCESSTYPE_EQ(pp, nonpp) \
281 static_assert(static_cast<int>(pp) == static_cast<int>(nonpp), \
282 "PP_NaClAppProcessType differs from NaClAppProcessType");
283 STATICALLY_CHECK_NACLAPPPROCESSTYPE_EQ(PP_UNKNOWN_NACL_PROCESS_TYPE
,
284 kUnknownNaClProcessType
);
285 STATICALLY_CHECK_NACLAPPPROCESSTYPE_EQ(PP_NATIVE_NACL_PROCESS_TYPE
,
286 kNativeNaClProcessType
);
287 STATICALLY_CHECK_NACLAPPPROCESSTYPE_EQ(PP_PNACL_PROCESS_TYPE
,
289 STATICALLY_CHECK_NACLAPPPROCESSTYPE_EQ(PP_PNACL_TRANSLATOR_PROCESS_TYPE
,
290 kPNaClTranslatorProcessType
);
291 STATICALLY_CHECK_NACLAPPPROCESSTYPE_EQ(PP_NUM_NACL_PROCESS_TYPES
,
292 kNumNaClProcessTypes
);
293 #undef STATICALLY_CHECK_NACLAPPPROCESSTYPE_EQ
294 DCHECK(pp_process_type
> PP_UNKNOWN_NACL_PROCESS_TYPE
&&
295 pp_process_type
< PP_NUM_NACL_PROCESS_TYPES
);
296 return static_cast<NaClAppProcessType
>(pp_process_type
);
299 // Launch NaCl's sel_ldr process.
300 void LaunchSelLdr(PP_Instance instance
,
301 PP_Bool main_service_runtime
,
302 const char* alleged_url
,
303 const PP_NaClFileInfo
* nexe_file_info
,
304 PP_Bool uses_nonsfi_mode
,
305 PP_Bool enable_ppapi_dev
,
306 PP_NaClAppProcessType pp_process_type
,
308 PP_CompletionCallback callback
) {
309 CHECK(ppapi::PpapiGlobals::Get()->GetMainThreadMessageLoop()->
310 BelongsToCurrentThread());
311 NaClAppProcessType process_type
= PP_ToNaClAppProcessType(pp_process_type
);
312 // Create the manifest service proxy here, so on error case, it will be
313 // destructed (without passing it to ManifestServiceChannel).
314 scoped_ptr
<ManifestServiceChannel::Delegate
> manifest_service_proxy(
315 new ManifestServiceProxy(instance
));
317 FileDescriptor result_socket
;
318 IPC::Sender
* sender
= content::RenderThread::Get();
321 // If the nexe uses ppapi APIs, we need a routing ID.
322 // To get the routing ID, we must be on the main thread.
323 // Some nexes do not use ppapi and launch from the background thread,
324 // so those nexes can skip finding a routing_id. Currently, that only
325 // applies to the PNaClTranslatorProcesses.
326 bool uses_ppapi
= process_type
!= kPNaClTranslatorProcessType
;
328 routing_id
= GetRoutingID(instance
);
330 if (nexe_file_info
->handle
!= PP_kInvalidFileHandle
) {
331 base::File
closer(nexe_file_info
->handle
);
333 ppapi::PpapiGlobals::Get()->GetMainThreadMessageLoop()->PostTask(
335 base::Bind(callback
.func
, callback
.user_data
,
336 static_cast<int32_t>(PP_ERROR_FAILED
)));
341 InstanceInfo instance_info
;
342 instance_info
.url
= GURL(alleged_url
);
344 uint32_t perm_bits
= ppapi::PERMISSION_NONE
;
345 // Conditionally block 'Dev' interfaces. We do this for the NaCl process, so
346 // it's clearer to developers when they are using 'Dev' inappropriately. We
347 // must also check on the trusted side of the proxy.
348 if (enable_ppapi_dev
)
349 perm_bits
|= ppapi::PERMISSION_DEV
;
350 instance_info
.permissions
=
351 ppapi::PpapiPermissions::GetForCommandLine(perm_bits
);
352 std::string error_message_string
;
353 NaClLaunchResult launch_result
;
355 IPC::PlatformFileForTransit nexe_for_transit
=
356 IPC::InvalidPlatformFileForTransit();
357 #if defined(OS_POSIX)
358 if (nexe_file_info
->handle
!= PP_kInvalidFileHandle
)
359 nexe_for_transit
= base::FileDescriptor(nexe_file_info
->handle
, true);
360 #elif defined(OS_WIN)
361 // Duplicate the handle on the browser side instead of the renderer.
362 // This is because BrokerGetFileForProcess isn't part of content/public, and
363 // it's simpler to do the duplication in the browser anyway.
364 nexe_for_transit
= nexe_file_info
->handle
;
366 #error Unsupported target platform.
368 if (!sender
->Send(new NaClHostMsg_LaunchNaCl(
370 instance_info
.url
.spec(),
372 nexe_file_info
->token_lo
,
373 nexe_file_info
->token_hi
,
376 PP_ToBool(uses_nonsfi_mode
),
379 &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
)));
387 if (!error_message_string
.empty()) {
388 if (PP_ToBool(main_service_runtime
)) {
389 NexeLoadManager
* load_manager
= NexeLoadManager::Get(instance
);
391 load_manager
->ReportLoadError(PP_NACL_ERROR_SEL_LDR_LAUNCH
,
392 "ServiceRuntime: failed to start",
393 error_message_string
);
396 ppapi::PpapiGlobals::Get()->GetMainThreadMessageLoop()->PostTask(
398 base::Bind(callback
.func
, callback
.user_data
,
399 static_cast<int32_t>(PP_ERROR_FAILED
)));
402 result_socket
= launch_result
.imc_channel_handle
;
403 instance_info
.channel_handle
= launch_result
.ppapi_ipc_channel_handle
;
404 instance_info
.plugin_pid
= launch_result
.plugin_pid
;
405 instance_info
.plugin_child_id
= launch_result
.plugin_child_id
;
407 // Don't save instance_info if channel handle is invalid.
408 if (IsValidChannelHandle(instance_info
.channel_handle
))
409 g_instance_info
.Get()[instance
] = instance_info
;
411 *(static_cast<NaClHandle
*>(imc_handle
)) = ToNativeHandle(result_socket
);
413 NexeLoadManager
* load_manager
= NexeLoadManager::Get(instance
);
414 DCHECK(load_manager
);
416 PostPPCompletionCallback(callback
, PP_ERROR_FAILED
);
417 base::SharedMemory::CloseHandle(launch_result
.crash_info_shmem_handle
);
421 // Store the crash information shared memory handle.
422 load_manager
->set_crash_info_shmem_handle(
423 launch_result
.crash_info_shmem_handle
);
425 // Create the trusted plugin channel.
426 if (IsValidChannelHandle(launch_result
.trusted_ipc_channel_handle
)) {
427 bool report_exit_status
= PP_ToBool(main_service_runtime
);
428 scoped_ptr
<TrustedPluginChannel
> trusted_plugin_channel(
429 new TrustedPluginChannel(
431 launch_result
.trusted_ipc_channel_handle
,
432 content::RenderThread::Get()->GetShutdownEvent(),
433 report_exit_status
));
434 load_manager
->set_trusted_plugin_channel(trusted_plugin_channel
.Pass());
436 PostPPCompletionCallback(callback
, PP_ERROR_FAILED
);
440 // Create the manifest service handle as well.
441 // For security hardening, disable the IPCs for open_resource() when they
442 // aren't needed. PNaCl doesn't expose open_resource().
444 process_type
== kNativeNaClProcessType
&&
445 IsValidChannelHandle(
446 launch_result
.manifest_service_ipc_channel_handle
)) {
447 scoped_ptr
<ManifestServiceChannel
> manifest_service_channel(
448 new ManifestServiceChannel(
449 launch_result
.manifest_service_ipc_channel_handle
,
450 base::Bind(&PostPPCompletionCallback
, callback
),
451 manifest_service_proxy
.Pass(),
452 content::RenderThread::Get()->GetShutdownEvent()));
453 load_manager
->set_manifest_service_channel(
454 manifest_service_channel
.Pass());
456 // Currently, manifest service works only on linux/non-SFI mode.
457 // On other platforms, the socket will not be created, and thus this
458 // condition needs to be handled as success.
459 PostPPCompletionCallback(callback
, PP_OK
);
463 PP_Bool
StartPpapiProxy(PP_Instance instance
) {
464 NexeLoadManager
* load_manager
= NexeLoadManager::Get(instance
);
465 DCHECK(load_manager
);
469 content::PepperPluginInstance
* plugin_instance
=
470 content::PepperPluginInstance::Get(instance
);
471 if (!plugin_instance
) {
472 DLOG(ERROR
) << "GetInstance() failed";
476 InstanceInfoMap
& map
= g_instance_info
.Get();
477 InstanceInfoMap::iterator it
= map
.find(instance
);
478 if (it
== map
.end()) {
479 DLOG(ERROR
) << "Could not find instance ID";
482 InstanceInfo instance_info
= it
->second
;
485 PP_ExternalPluginResult result
= plugin_instance
->SwitchToOutOfProcessProxy(
486 base::FilePath().AppendASCII(instance_info
.url
.spec()),
487 instance_info
.permissions
,
488 instance_info
.channel_handle
,
489 instance_info
.plugin_pid
,
490 instance_info
.plugin_child_id
);
492 if (result
== PP_EXTERNAL_PLUGIN_OK
) {
493 // Log the amound of time that has passed between the trusted plugin being
494 // initialized and the untrusted plugin being initialized. This is
495 // (roughly) the cost of using NaCl, in terms of startup time.
496 load_manager
->ReportStartupOverhead();
498 } else if (result
== PP_EXTERNAL_PLUGIN_ERROR_MODULE
) {
499 load_manager
->ReportLoadError(PP_NACL_ERROR_START_PROXY_MODULE
,
500 "could not initialize module.");
501 } else if (result
== PP_EXTERNAL_PLUGIN_ERROR_INSTANCE
) {
502 load_manager
->ReportLoadError(PP_NACL_ERROR_START_PROXY_MODULE
,
503 "could not create instance.");
508 int UrandomFD(void) {
509 #if defined(OS_POSIX)
510 return base::GetUrandomFD();
516 PP_Bool
Are3DInterfacesDisabled() {
517 return PP_FromBool(CommandLine::ForCurrentProcess()->HasSwitch(
518 switches::kDisable3DAPIs
));
521 int32_t BrokerDuplicateHandle(PP_FileHandle source_handle
,
523 PP_FileHandle
* target_handle
,
524 uint32_t desired_access
,
527 return content::BrokerDuplicateHandle(source_handle
, process_id
,
528 target_handle
, desired_access
,
535 // Convert a URL to a filename for GetReadonlyPnaclFd.
536 // Must be kept in sync with PnaclCanOpenFile() in
537 // components/nacl/browser/nacl_file_host.cc.
538 std::string
PnaclComponentURLToFilename(const std::string
& url
) {
539 // PNaCl component URLs aren't arbitrary URLs; they are always either
540 // generated from ManifestResolveKey or PnaclResources::ReadResourceInfo.
541 // So, it's safe to just use string parsing operations here instead of
543 DCHECK(StartsWithASCII(url
, kPNaClTranslatorBaseUrl
, true));
544 std::string r
= url
.substr(std::string(kPNaClTranslatorBaseUrl
).length());
546 // Use white-listed-chars.
548 static const char* white_list
= "abcdefghijklmnopqrstuvwxyz0123456789_";
549 replace_pos
= r
.find_first_not_of(white_list
);
550 while(replace_pos
!= std::string::npos
) {
551 r
= r
.replace(replace_pos
, 1, "_");
552 replace_pos
= r
.find_first_not_of(white_list
);
557 PP_FileHandle
GetReadonlyPnaclFd(const char* url
,
560 uint64_t* nonce_hi
) {
561 std::string filename
= PnaclComponentURLToFilename(url
);
562 IPC::PlatformFileForTransit out_fd
= IPC::InvalidPlatformFileForTransit();
563 IPC::Sender
* sender
= content::RenderThread::Get();
565 if (!sender
->Send(new NaClHostMsg_GetReadonlyPnaclFD(
566 std::string(filename
), is_executable
,
567 &out_fd
, nonce_lo
, nonce_hi
))) {
568 return PP_kInvalidFileHandle
;
570 if (out_fd
== IPC::InvalidPlatformFileForTransit()) {
571 return PP_kInvalidFileHandle
;
573 return IPC::PlatformFileForTransitToPlatformFile(out_fd
);
576 void GetReadExecPnaclFd(const char* url
,
577 PP_NaClFileInfo
* out_file_info
) {
578 *out_file_info
= kInvalidNaClFileInfo
;
579 out_file_info
->handle
= GetReadonlyPnaclFd(url
, true /* is_executable */,
580 &out_file_info
->token_lo
,
581 &out_file_info
->token_hi
);
584 PP_FileHandle
CreateTemporaryFile(PP_Instance instance
) {
585 IPC::PlatformFileForTransit transit_fd
= IPC::InvalidPlatformFileForTransit();
586 IPC::Sender
* sender
= content::RenderThread::Get();
588 if (!sender
->Send(new NaClHostMsg_NaClCreateTemporaryFile(
590 return PP_kInvalidFileHandle
;
593 if (transit_fd
== IPC::InvalidPlatformFileForTransit()) {
594 return PP_kInvalidFileHandle
;
597 return IPC::PlatformFileForTransitToPlatformFile(transit_fd
);
600 int32_t GetNumberOfProcessors() {
601 IPC::Sender
* sender
= content::RenderThread::Get();
603 int32_t num_processors
= 1;
604 return sender
->Send(new NaClHostMsg_NaClGetNumProcessors(&num_processors
)) ?
608 PP_Bool
PPIsNonSFIModeEnabled() {
609 return PP_FromBool(IsNonSFIModeEnabled());
612 void GetNexeFd(PP_Instance instance
,
613 const std::string
& pexe_url
,
615 const base::Time
& last_modified_time
,
616 const std::string
& etag
,
617 bool has_no_store_header
,
618 base::Callback
<void(int32_t, bool, PP_FileHandle
)> callback
) {
619 if (!InitializePnaclResourceHost()) {
620 ppapi::PpapiGlobals::Get()->GetMainThreadMessageLoop()->PostTask(
623 static_cast<int32_t>(PP_ERROR_FAILED
),
625 PP_kInvalidFileHandle
));
629 PnaclCacheInfo cache_info
;
630 cache_info
.pexe_url
= GURL(pexe_url
);
631 // TODO(dschuff): Get this value from the pnacl json file after it
632 // rolls in from NaCl.
633 cache_info
.abi_version
= 1;
634 cache_info
.opt_level
= opt_level
;
635 cache_info
.last_modified
= last_modified_time
;
636 cache_info
.etag
= etag
;
637 cache_info
.has_no_store_header
= has_no_store_header
;
638 cache_info
.sandbox_isa
= GetSandboxArch();
639 cache_info
.extra_flags
= GetCpuFeatures();
641 g_pnacl_resource_host
.Get()->RequestNexeFd(
642 GetRoutingID(instance
),
648 void ReportTranslationFinished(PP_Instance instance
,
652 int64_t compile_time_us
) {
653 if (success
== PP_TRUE
) {
654 static const int32_t kUnknownOptLevel
= 4;
655 if (opt_level
< 0 || opt_level
> 3)
656 opt_level
= kUnknownOptLevel
;
657 HistogramEnumerate("NaCl.Options.PNaCl.OptLevel",
659 kUnknownOptLevel
+ 1);
660 HistogramKBPerSec("NaCl.Perf.PNaClLoadTime.CompileKBPerSec",
663 HistogramSizeKB("NaCl.Perf.Size.Pexe", pexe_size
/ 1024);
665 NexeLoadManager
* load_manager
= NexeLoadManager::Get(instance
);
667 base::TimeDelta total_time
= base::Time::Now() -
668 load_manager
->pnacl_start_time();
669 HistogramTimeTranslation("NaCl.Perf.PNaClLoadTime.TotalUncachedTime",
670 total_time
.InMilliseconds());
671 HistogramKBPerSec("NaCl.Perf.PNaClLoadTime.TotalUncachedKBPerSec",
673 total_time
.InMicroseconds());
677 // If the resource host isn't initialized, don't try to do that here.
678 // Just return because something is already very wrong.
679 if (g_pnacl_resource_host
.Get().get() == NULL
)
681 g_pnacl_resource_host
.Get()->ReportTranslationFinished(instance
, success
);
684 PP_FileHandle
OpenNaClExecutable(PP_Instance instance
,
685 const char* file_url
,
687 uint64_t* nonce_hi
) {
688 // Fast path only works for installed file URLs.
690 if (!gurl
.SchemeIs("chrome-extension"))
691 return PP_kInvalidFileHandle
;
693 content::PepperPluginInstance
* plugin_instance
=
694 content::PepperPluginInstance::Get(instance
);
695 if (!plugin_instance
)
696 return PP_kInvalidFileHandle
;
697 // IMPORTANT: Make sure the document can request the given URL. If we don't
698 // check, a malicious app could probe the extension system. This enforces a
699 // same-origin policy which prevents the app from requesting resources from
701 blink::WebSecurityOrigin security_origin
=
702 plugin_instance
->GetContainer()->element().document().securityOrigin();
703 if (!security_origin
.canRequest(gurl
))
704 return PP_kInvalidFileHandle
;
706 IPC::PlatformFileForTransit out_fd
= IPC::InvalidPlatformFileForTransit();
707 IPC::Sender
* sender
= content::RenderThread::Get();
711 base::FilePath file_path
;
713 new NaClHostMsg_OpenNaClExecutable(GetRoutingID(instance
),
718 return PP_kInvalidFileHandle
;
721 if (out_fd
== IPC::InvalidPlatformFileForTransit())
722 return PP_kInvalidFileHandle
;
724 return IPC::PlatformFileForTransitToPlatformFile(out_fd
);
727 void DispatchEvent(PP_Instance instance
,
728 PP_NaClEventType event_type
,
729 const char *resource_url
,
730 PP_Bool length_is_computable
,
731 uint64_t loaded_bytes
,
732 uint64_t total_bytes
) {
733 ProgressEvent
event(event_type
,
735 PP_ToBool(length_is_computable
),
738 DispatchProgressEvent(instance
, event
);
741 void ReportLoadSuccess(PP_Instance instance
,
742 uint64_t loaded_bytes
,
743 uint64_t total_bytes
) {
744 NexeLoadManager
* load_manager
= NexeLoadManager::Get(instance
);
746 load_manager
->ReportLoadSuccess(load_manager
->program_url(),
752 void ReportLoadError(PP_Instance instance
,
754 const char* error_message
) {
755 NexeLoadManager
* load_manager
= NexeLoadManager::Get(instance
);
757 load_manager
->ReportLoadError(error
, error_message
);
760 void ReportLoadAbort(PP_Instance instance
) {
761 NexeLoadManager
* load_manager
= NexeLoadManager::Get(instance
);
763 load_manager
->ReportLoadAbort();
766 void InstanceCreated(PP_Instance instance
) {
767 NexeLoadManager::Create(instance
);
770 void InstanceDestroyed(PP_Instance instance
) {
771 DeleteJsonManifest(instance
);
772 NexeLoadManager::Delete(instance
);
775 PP_Bool
NaClDebugEnabledForURL(const char* alleged_nmf_url
) {
776 if (!CommandLine::ForCurrentProcess()->HasSwitch(switches::kEnableNaClDebug
))
778 IPC::Sender
* sender
= content::RenderThread::Get();
780 bool should_debug
= false;
782 sender
->Send(new NaClHostMsg_NaClDebugEnabledForURL(GURL(alleged_nmf_url
),
787 void LogToConsole(PP_Instance instance
, const char* message
) {
788 NexeLoadManager
* load_manager
= NexeLoadManager::Get(instance
);
789 DCHECK(load_manager
);
791 load_manager
->LogToConsole(std::string(message
));
794 PP_NaClReadyState
GetNaClReadyState(PP_Instance instance
) {
795 NexeLoadManager
* load_manager
= NexeLoadManager::Get(instance
);
796 DCHECK(load_manager
);
798 return load_manager
->nacl_ready_state();
799 return PP_NACL_READY_STATE_UNSENT
;
802 void Vlog(const char* message
) {
806 void InitializePlugin(PP_Instance instance
,
809 const char* argv
[]) {
810 NexeLoadManager
* load_manager
= NexeLoadManager::Get(instance
);
811 DCHECK(load_manager
);
813 load_manager
->InitializePlugin(argc
, argn
, argv
);
816 int64_t GetNexeSize(PP_Instance instance
) {
817 NexeLoadManager
* load_manager
= NexeLoadManager::Get(instance
);
818 DCHECK(load_manager
);
820 return load_manager
->nexe_size();
824 void DownloadManifestToBuffer(PP_Instance instance
,
825 struct PP_CompletionCallback callback
);
827 bool CreateJsonManifest(PP_Instance instance
,
828 const std::string
& manifest_url
,
829 const std::string
& manifest_data
);
831 void RequestNaClManifest(PP_Instance instance
,
832 PP_CompletionCallback callback
) {
833 NexeLoadManager
* load_manager
= NexeLoadManager::Get(instance
);
834 DCHECK(load_manager
);
836 ppapi::PpapiGlobals::Get()->GetMainThreadMessageLoop()->PostTask(
838 base::Bind(callback
.func
, callback
.user_data
,
839 static_cast<int32_t>(PP_ERROR_FAILED
)));
843 std::string url
= load_manager
->GetManifestURLArgument();
844 if (url
.empty() || !load_manager
->RequestNaClManifest(url
)) {
845 ppapi::PpapiGlobals::Get()->GetMainThreadMessageLoop()->PostTask(
847 base::Bind(callback
.func
, callback
.user_data
,
848 static_cast<int32_t>(PP_ERROR_FAILED
)));
852 const GURL
& base_url
= load_manager
->manifest_base_url();
853 if (base_url
.SchemeIs("data")) {
855 std::string mime_type
;
858 int32_t error
= PP_ERROR_FAILED
;
859 if (net::DataURL::Parse(gurl
, &mime_type
, &charset
, &data
)) {
860 if (data
.size() <= ManifestDownloader::kNaClManifestMaxFileBytes
) {
861 if (CreateJsonManifest(instance
, base_url
.spec(), data
))
864 load_manager
->ReportLoadError(PP_NACL_ERROR_MANIFEST_TOO_LARGE
,
865 "manifest file too large.");
868 load_manager
->ReportLoadError(PP_NACL_ERROR_MANIFEST_LOAD_URL
,
869 "could not load manifest url.");
871 ppapi::PpapiGlobals::Get()->GetMainThreadMessageLoop()->PostTask(
873 base::Bind(callback
.func
, callback
.user_data
, error
));
875 DownloadManifestToBuffer(instance
, callback
);
879 PP_Var
GetManifestBaseURL(PP_Instance instance
) {
880 NexeLoadManager
* load_manager
= NexeLoadManager::Get(instance
);
881 DCHECK(load_manager
);
883 return PP_MakeUndefined();
884 const GURL
& gurl
= load_manager
->manifest_base_url();
885 if (!gurl
.is_valid())
886 return PP_MakeUndefined();
887 return ppapi::StringVar::StringToPPVar(gurl
.spec());
890 void ProcessNaClManifest(PP_Instance instance
, const char* program_url
) {
891 nacl::NexeLoadManager
* load_manager
= NexeLoadManager::Get(instance
);
893 load_manager
->ProcessNaClManifest(program_url
);
896 PP_Bool
DevInterfacesEnabled(PP_Instance instance
) {
897 nacl::NexeLoadManager
* load_manager
= NexeLoadManager::Get(instance
);
899 return PP_FromBool(load_manager
->DevInterfacesEnabled());
903 void DownloadManifestToBufferCompletion(PP_Instance instance
,
904 struct PP_CompletionCallback callback
,
905 base::Time start_time
,
906 PP_NaClError pp_nacl_error
,
907 const std::string
& data
);
909 void DownloadManifestToBuffer(PP_Instance instance
,
910 struct PP_CompletionCallback callback
) {
911 nacl::NexeLoadManager
* load_manager
= NexeLoadManager::Get(instance
);
912 DCHECK(load_manager
);
913 content::PepperPluginInstance
* plugin_instance
=
914 content::PepperPluginInstance::Get(instance
);
915 if (!load_manager
|| !plugin_instance
) {
916 ppapi::PpapiGlobals::Get()->GetMainThreadMessageLoop()->PostTask(
918 base::Bind(callback
.func
, callback
.user_data
,
919 static_cast<int32_t>(PP_ERROR_FAILED
)));
921 const blink::WebDocument
& document
=
922 plugin_instance
->GetContainer()->element().document();
924 const GURL
& gurl
= load_manager
->manifest_base_url();
925 scoped_ptr
<blink::WebURLLoader
> url_loader(
926 CreateWebURLLoader(document
, gurl
));
927 blink::WebURLRequest request
= CreateWebURLRequest(document
, gurl
);
929 // ManifestDownloader deletes itself after invoking the callback.
930 ManifestDownloader
* manifest_downloader
= new ManifestDownloader(
932 load_manager
->is_installed(),
933 base::Bind(DownloadManifestToBufferCompletion
,
934 instance
, callback
, base::Time::Now()));
935 manifest_downloader
->Load(request
);
938 void DownloadManifestToBufferCompletion(PP_Instance instance
,
939 struct PP_CompletionCallback callback
,
940 base::Time start_time
,
941 PP_NaClError pp_nacl_error
,
942 const std::string
& data
) {
943 base::TimeDelta download_time
= base::Time::Now() - start_time
;
944 HistogramTimeSmall("NaCl.Perf.StartupTime.ManifestDownload",
945 download_time
.InMilliseconds());
947 nacl::NexeLoadManager
* load_manager
= NexeLoadManager::Get(instance
);
949 callback
.func(callback
.user_data
, PP_ERROR_ABORTED
);
954 switch (pp_nacl_error
) {
955 case PP_NACL_ERROR_LOAD_SUCCESS
:
958 case PP_NACL_ERROR_MANIFEST_LOAD_URL
:
959 pp_error
= PP_ERROR_FAILED
;
960 load_manager
->ReportLoadError(PP_NACL_ERROR_MANIFEST_LOAD_URL
,
961 "could not load manifest url.");
963 case PP_NACL_ERROR_MANIFEST_TOO_LARGE
:
964 pp_error
= PP_ERROR_FILETOOBIG
;
965 load_manager
->ReportLoadError(PP_NACL_ERROR_MANIFEST_TOO_LARGE
,
966 "manifest file too large.");
968 case PP_NACL_ERROR_MANIFEST_NOACCESS_URL
:
969 pp_error
= PP_ERROR_NOACCESS
;
970 load_manager
->ReportLoadError(PP_NACL_ERROR_MANIFEST_NOACCESS_URL
,
971 "access to manifest url was denied.");
975 pp_error
= PP_ERROR_FAILED
;
976 load_manager
->ReportLoadError(PP_NACL_ERROR_MANIFEST_LOAD_URL
,
977 "could not load manifest url.");
980 if (pp_error
== PP_OK
) {
981 std::string base_url
= load_manager
->manifest_base_url().spec();
982 if (!CreateJsonManifest(instance
, base_url
, data
))
983 pp_error
= PP_ERROR_FAILED
;
985 callback
.func(callback
.user_data
, pp_error
);
988 bool CreateJsonManifest(PP_Instance instance
,
989 const std::string
& manifest_url
,
990 const std::string
& manifest_data
) {
991 HistogramSizeKB("NaCl.Perf.Size.Manifest",
992 static_cast<int32_t>(manifest_data
.length() / 1024));
994 nacl::NexeLoadManager
* load_manager
= NexeLoadManager::Get(instance
);
998 const char* isa_type
;
999 if (load_manager
->IsPNaCl())
1000 isa_type
= kPortableArch
;
1002 isa_type
= GetSandboxArch();
1004 scoped_ptr
<nacl::JsonManifest
> j(
1005 new nacl::JsonManifest(
1006 manifest_url
.c_str(),
1008 IsNonSFIModeEnabled(),
1009 PP_ToBool(NaClDebugEnabledForURL(manifest_url
.c_str()))));
1010 JsonManifest::ErrorInfo error_info
;
1011 if (j
->Init(manifest_data
.c_str(), &error_info
)) {
1012 AddJsonManifest(instance
, j
.Pass());
1015 load_manager
->ReportLoadError(error_info
.error
, error_info
.string
);
1019 PP_Bool
ManifestGetProgramURL(PP_Instance instance
,
1020 PP_Var
* pp_full_url
,
1021 PP_PNaClOptions
* pnacl_options
,
1022 PP_Bool
* pp_uses_nonsfi_mode
) {
1023 nacl::NexeLoadManager
* load_manager
= NexeLoadManager::Get(instance
);
1025 JsonManifest
* manifest
= GetJsonManifest(instance
);
1026 if (manifest
== NULL
)
1029 bool uses_nonsfi_mode
;
1030 std::string full_url
;
1031 JsonManifest::ErrorInfo error_info
;
1032 if (manifest
->GetProgramURL(&full_url
, pnacl_options
, &uses_nonsfi_mode
,
1034 *pp_full_url
= ppapi::StringVar::StringToPPVar(full_url
);
1035 *pp_uses_nonsfi_mode
= PP_FromBool(uses_nonsfi_mode
);
1040 load_manager
->ReportLoadError(error_info
.error
, error_info
.string
);
1044 bool ManifestResolveKey(PP_Instance instance
,
1045 bool is_helper_process
,
1046 const std::string
& key
,
1047 std::string
* full_url
,
1048 PP_PNaClOptions
* pnacl_options
) {
1049 // For "helper" processes (llc and ld, for PNaCl translation), we resolve
1050 // keys manually as there is no existing .nmf file to parse.
1051 if (is_helper_process
) {
1052 pnacl_options
->translate
= PP_FALSE
;
1053 // We can only resolve keys in the files/ namespace.
1054 const std::string kFilesPrefix
= "files/";
1055 if (key
.find(kFilesPrefix
) == std::string::npos
) {
1056 nacl::NexeLoadManager
* load_manager
= NexeLoadManager::Get(instance
);
1058 load_manager
->ReportLoadError(PP_NACL_ERROR_MANIFEST_RESOLVE_URL
,
1059 "key did not start with files/");
1062 std::string key_basename
= key
.substr(kFilesPrefix
.length());
1063 *full_url
= std::string(kPNaClTranslatorBaseUrl
) + GetSandboxArch() + "/" +
1068 JsonManifest
* manifest
= GetJsonManifest(instance
);
1069 if (manifest
== NULL
)
1072 return manifest
->ResolveKey(key
, full_url
, pnacl_options
);
1075 PP_Bool
GetPNaClResourceInfo(PP_Instance instance
,
1076 PP_Var
* llc_tool_name
,
1077 PP_Var
* ld_tool_name
) {
1078 static const char kFilename
[] = "chrome://pnacl-translator/pnacl.json";
1079 NexeLoadManager
* load_manager
= NexeLoadManager::Get(instance
);
1080 DCHECK(load_manager
);
1084 uint64_t nonce_lo
= 0;
1085 uint64_t nonce_hi
= 0;
1086 base::File
file(GetReadonlyPnaclFd(kFilename
, false /* is_executable */,
1087 &nonce_lo
, &nonce_hi
));
1088 if (!file
.IsValid()) {
1089 load_manager
->ReportLoadError(
1090 PP_NACL_ERROR_PNACL_RESOURCE_FETCH
,
1091 "The Portable Native Client (pnacl) component is not "
1092 "installed. Please consult chrome://components for more "
1097 base::File::Info file_info
;
1098 if (!file
.GetInfo(&file_info
)) {
1099 load_manager
->ReportLoadError(
1100 PP_NACL_ERROR_PNACL_RESOURCE_FETCH
,
1101 std::string("GetPNaClResourceInfo, GetFileInfo failed for: ") +
1106 if (file_info
.size
> 1 << 20) {
1107 load_manager
->ReportLoadError(
1108 PP_NACL_ERROR_PNACL_RESOURCE_FETCH
,
1109 std::string("GetPNaClResourceInfo, file too large: ") + kFilename
);
1113 scoped_ptr
<char[]> buffer(new char[file_info
.size
+ 1]);
1114 if (buffer
.get() == NULL
) {
1115 load_manager
->ReportLoadError(
1116 PP_NACL_ERROR_PNACL_RESOURCE_FETCH
,
1117 std::string("GetPNaClResourceInfo, couldn't allocate for: ") +
1122 int rc
= file
.Read(0, buffer
.get(), file_info
.size
);
1124 load_manager
->ReportLoadError(
1125 PP_NACL_ERROR_PNACL_RESOURCE_FETCH
,
1126 std::string("GetPNaClResourceInfo, reading failed for: ") + kFilename
);
1130 // Null-terminate the bytes we we read from the file.
1131 buffer
.get()[rc
] = 0;
1133 // Expect the JSON file to contain a top-level object (dictionary).
1134 Json::Reader json_reader
;
1135 Json::Value json_data
;
1136 if (!json_reader
.parse(buffer
.get(), json_data
)) {
1137 load_manager
->ReportLoadError(
1138 PP_NACL_ERROR_PNACL_RESOURCE_FETCH
,
1139 std::string("Parsing resource info failed: JSON parse error: ") +
1140 json_reader
.getFormattedErrorMessages());
1144 if (!json_data
.isObject()) {
1145 load_manager
->ReportLoadError(
1146 PP_NACL_ERROR_PNACL_RESOURCE_FETCH
,
1147 "Parsing resource info failed: Malformed JSON dictionary");
1151 if (json_data
.isMember("pnacl-llc-name")) {
1152 Json::Value json_name
= json_data
["pnacl-llc-name"];
1153 if (json_name
.isString()) {
1154 std::string llc_tool_name_str
= json_name
.asString();
1155 *llc_tool_name
= ppapi::StringVar::StringToPPVar(llc_tool_name_str
);
1159 if (json_data
.isMember("pnacl-ld-name")) {
1160 Json::Value json_name
= json_data
["pnacl-ld-name"];
1161 if (json_name
.isString()) {
1162 std::string ld_tool_name_str
= json_name
.asString();
1163 *ld_tool_name
= ppapi::StringVar::StringToPPVar(ld_tool_name_str
);
1169 PP_Var
GetCpuFeatureAttrs() {
1170 return ppapi::StringVar::StringToPPVar(GetCpuFeatures());
1173 // Encapsulates some of the state for a call to DownloadNexe to prevent
1174 // argument lists from getting too long.
1175 struct DownloadNexeRequest
{
1176 PP_Instance instance
;
1178 PP_CompletionCallback callback
;
1179 base::Time start_time
;
1182 // A utility class to ensure that we don't send progress events more often than
1183 // every 10ms for a given file.
1184 class ProgressEventRateLimiter
{
1186 explicit ProgressEventRateLimiter(PP_Instance instance
)
1187 : instance_(instance
) { }
1189 void ReportProgress(const std::string
& url
,
1190 int64_t total_bytes_received
,
1191 int64_t total_bytes_to_be_received
) {
1192 base::Time now
= base::Time::Now();
1193 if (now
- last_event_
> base::TimeDelta::FromMilliseconds(10)) {
1194 DispatchProgressEvent(instance_
,
1195 ProgressEvent(PP_NACL_EVENT_PROGRESS
,
1197 total_bytes_to_be_received
>= 0,
1198 total_bytes_received
,
1199 total_bytes_to_be_received
));
1205 PP_Instance instance_
;
1206 base::Time last_event_
;
1209 void DownloadNexeCompletion(const DownloadNexeRequest
& request
,
1210 PP_NaClFileInfo
* out_file_info
,
1211 FileDownloader::Status status
,
1212 base::File target_file
,
1215 void DownloadNexe(PP_Instance instance
,
1217 PP_NaClFileInfo
* out_file_info
,
1218 PP_CompletionCallback callback
) {
1220 CHECK(out_file_info
);
1221 DownloadNexeRequest request
;
1222 request
.instance
= instance
;
1224 request
.callback
= callback
;
1225 request
.start_time
= base::Time::Now();
1227 // Try the fast path for retrieving the file first.
1228 PP_FileHandle handle
= OpenNaClExecutable(instance
,
1230 &out_file_info
->token_lo
,
1231 &out_file_info
->token_hi
);
1232 if (handle
!= PP_kInvalidFileHandle
) {
1233 DownloadNexeCompletion(request
,
1235 FileDownloader::SUCCESS
,
1241 // The fast path didn't work, we'll fetch the file using URLLoader and write
1242 // it to local storage.
1243 base::File
target_file(CreateTemporaryFile(instance
));
1246 content::PepperPluginInstance
* plugin_instance
=
1247 content::PepperPluginInstance::Get(instance
);
1248 if (!plugin_instance
) {
1249 ppapi::PpapiGlobals::Get()->GetMainThreadMessageLoop()->PostTask(
1251 base::Bind(callback
.func
, callback
.user_data
,
1252 static_cast<int32_t>(PP_ERROR_FAILED
)));
1254 const blink::WebDocument
& document
=
1255 plugin_instance
->GetContainer()->element().document();
1256 scoped_ptr
<blink::WebURLLoader
> url_loader(
1257 CreateWebURLLoader(document
, gurl
));
1258 blink::WebURLRequest url_request
= CreateWebURLRequest(document
, gurl
);
1260 ProgressEventRateLimiter
* tracker
= new ProgressEventRateLimiter(instance
);
1262 // FileDownloader deletes itself after invoking DownloadNexeCompletion.
1263 FileDownloader
* file_downloader
= new FileDownloader(
1266 base::Bind(&DownloadNexeCompletion
, request
, out_file_info
),
1267 base::Bind(&ProgressEventRateLimiter::ReportProgress
,
1268 base::Owned(tracker
), std::string(url
)));
1269 file_downloader
->Load(url_request
);
1272 void DownloadNexeCompletion(const DownloadNexeRequest
& request
,
1273 PP_NaClFileInfo
* out_file_info
,
1274 FileDownloader::Status status
,
1275 base::File target_file
,
1277 int32_t pp_error
= FileDownloaderToPepperError(status
);
1278 int64_t bytes_read
= -1;
1279 if (pp_error
== PP_OK
&& target_file
.IsValid()) {
1280 base::File::Info info
;
1281 if (target_file
.GetInfo(&info
))
1282 bytes_read
= info
.size
;
1285 if (bytes_read
== -1) {
1286 target_file
.Close();
1287 pp_error
= PP_ERROR_FAILED
;
1290 base::TimeDelta download_time
= base::Time::Now() - request
.start_time
;
1292 NexeLoadManager
* load_manager
= NexeLoadManager::Get(request
.instance
);
1294 load_manager
->NexeFileDidOpen(pp_error
,
1302 if (pp_error
== PP_OK
&& target_file
.IsValid())
1303 out_file_info
->handle
= target_file
.TakePlatformFile();
1305 out_file_info
->handle
= PP_kInvalidFileHandle
;
1307 request
.callback
.func(request
.callback
.user_data
, pp_error
);
1310 void DownloadFileCompletion(
1311 const DownloadFileCallback
& callback
,
1312 FileDownloader::Status status
,
1315 int32_t pp_error
= FileDownloaderToPepperError(status
);
1316 PP_NaClFileInfo file_info
;
1317 if (pp_error
== PP_OK
) {
1318 file_info
.handle
= file
.TakePlatformFile();
1319 file_info
.token_lo
= 0;
1320 file_info
.token_hi
= 0;
1322 file_info
= kInvalidNaClFileInfo
;
1325 callback
.Run(pp_error
, file_info
);
1328 void DownloadFile(PP_Instance instance
,
1329 const std::string
& url
,
1330 const DownloadFileCallback
& callback
) {
1331 DCHECK(ppapi::PpapiGlobals::Get()->GetMainThreadMessageLoop()->
1332 BelongsToCurrentThread());
1334 NexeLoadManager
* load_manager
= NexeLoadManager::Get(instance
);
1335 DCHECK(load_manager
);
1336 if (!load_manager
) {
1337 base::MessageLoop::current()->PostTask(
1339 base::Bind(callback
,
1340 static_cast<int32_t>(PP_ERROR_FAILED
),
1341 kInvalidNaClFileInfo
));
1345 // Handle special PNaCl support files which are installed on the user's
1347 if (url
.find(kPNaClTranslatorBaseUrl
, 0) == 0) {
1348 PP_NaClFileInfo file_info
= kInvalidNaClFileInfo
;
1349 PP_FileHandle handle
= GetReadonlyPnaclFd(url
.c_str(),
1350 false /* is_executable */,
1351 &file_info
.token_lo
,
1352 &file_info
.token_hi
);
1353 if (handle
== PP_kInvalidFileHandle
) {
1354 base::MessageLoop::current()->PostTask(
1356 base::Bind(callback
,
1357 static_cast<int32_t>(PP_ERROR_FAILED
),
1358 kInvalidNaClFileInfo
));
1361 file_info
.handle
= handle
;
1362 base::MessageLoop::current()->PostTask(
1364 base::Bind(callback
, static_cast<int32_t>(PP_OK
), file_info
));
1368 // We have to ensure that this url resolves relative to the plugin base url
1369 // before downloading it.
1370 const GURL
& test_gurl
= load_manager
->plugin_base_url().Resolve(url
);
1371 if (!test_gurl
.is_valid()) {
1372 base::MessageLoop::current()->PostTask(
1374 base::Bind(callback
,
1375 static_cast<int32_t>(PP_ERROR_FAILED
),
1376 kInvalidNaClFileInfo
));
1380 // Try the fast path for retrieving the file first.
1381 uint64_t file_token_lo
= 0;
1382 uint64_t file_token_hi
= 0;
1383 PP_FileHandle file_handle
= OpenNaClExecutable(instance
,
1387 if (file_handle
!= PP_kInvalidFileHandle
) {
1388 PP_NaClFileInfo file_info
;
1389 file_info
.handle
= file_handle
;
1390 file_info
.token_lo
= file_token_lo
;
1391 file_info
.token_hi
= file_token_hi
;
1392 base::MessageLoop::current()->PostTask(
1394 base::Bind(callback
, static_cast<int32_t>(PP_OK
), file_info
));
1398 // The fast path didn't work, we'll fetch the file using URLLoader and write
1399 // it to local storage.
1400 base::File
target_file(CreateTemporaryFile(instance
));
1403 content::PepperPluginInstance
* plugin_instance
=
1404 content::PepperPluginInstance::Get(instance
);
1405 if (!plugin_instance
) {
1406 base::MessageLoop::current()->PostTask(
1408 base::Bind(callback
,
1409 static_cast<int32_t>(PP_ERROR_FAILED
),
1410 kInvalidNaClFileInfo
));
1412 const blink::WebDocument
& document
=
1413 plugin_instance
->GetContainer()->element().document();
1414 scoped_ptr
<blink::WebURLLoader
> url_loader(
1415 CreateWebURLLoader(document
, gurl
));
1416 blink::WebURLRequest url_request
= CreateWebURLRequest(document
, gurl
);
1418 ProgressEventRateLimiter
* tracker
= new ProgressEventRateLimiter(instance
);
1420 // FileDownloader deletes itself after invoking DownloadNexeCompletion.
1421 FileDownloader
* file_downloader
= new FileDownloader(
1424 base::Bind(&DownloadFileCompletion
, callback
),
1425 base::Bind(&ProgressEventRateLimiter::ReportProgress
,
1426 base::Owned(tracker
), std::string(url
)));
1427 file_downloader
->Load(url_request
);
1430 void ReportSelLdrStatus(PP_Instance instance
,
1431 int32_t load_status
,
1432 int32_t max_status
) {
1433 HistogramEnumerate("NaCl.LoadStatus.SelLdr", load_status
, max_status
);
1434 NexeLoadManager
* load_manager
= NexeLoadManager::Get(instance
);
1435 DCHECK(load_manager
);
1439 // Gather data to see if being installed changes load outcomes.
1440 const char* name
= load_manager
->is_installed() ?
1441 "NaCl.LoadStatus.SelLdr.InstalledApp" :
1442 "NaCl.LoadStatus.SelLdr.NotInstalledApp";
1443 HistogramEnumerate(name
, load_status
, max_status
);
1446 void LogTranslateTime(const char* histogram_name
,
1447 int64_t time_in_us
) {
1448 ppapi::PpapiGlobals::Get()->GetMainThreadMessageLoop()->PostTask(
1450 base::Bind(&HistogramTimeTranslation
,
1451 std::string(histogram_name
),
1452 time_in_us
/ 1000));
1455 void DidOpenManifestEntry(PP_NaClFileInfo
* out_file_info
,
1456 PP_CompletionCallback callback
,
1458 const PP_NaClFileInfo
& file_info
) {
1459 if (pp_error
== PP_OK
)
1460 *out_file_info
= file_info
;
1461 callback
.func(callback
.user_data
, pp_error
);
1464 void OpenManifestEntry(PP_Instance instance
,
1465 PP_Bool is_helper_process
,
1467 PP_NaClFileInfo
* out_file_info
,
1468 PP_CompletionCallback callback
) {
1470 PP_PNaClOptions pnacl_options
;
1471 pnacl_options
.translate
= PP_FALSE
;
1472 pnacl_options
.is_debug
= PP_FALSE
;
1473 pnacl_options
.opt_level
= 2;
1474 if (!ManifestResolveKey(instance
,
1475 PP_ToBool(is_helper_process
),
1479 PostPPCompletionCallback(callback
, PP_ERROR_FAILED
);
1482 // TODO(teravest): Make a type like PP_NaClFileInfo to use for DownloadFile
1483 // that would close the file handle on destruction.
1484 DownloadFile(instance
, url
,
1485 base::Bind(&DidOpenManifestEntry
, out_file_info
, callback
));
1488 void SetPNaClStartTime(PP_Instance instance
) {
1489 NexeLoadManager
* load_manager
= NexeLoadManager::Get(instance
);
1491 load_manager
->set_pnacl_start_time(base::Time::Now());
1494 // PexeDownloader is responsible for deleting itself when the download
1496 class PexeDownloader
: public blink::WebURLLoaderClient
{
1498 PexeDownloader(PP_Instance instance
,
1499 scoped_ptr
<blink::WebURLLoader
> url_loader
,
1500 const std::string
& pexe_url
,
1501 int32_t pexe_opt_level
,
1502 const PPP_PexeStreamHandler
* stream_handler
,
1503 void* stream_handler_user_data
)
1504 : instance_(instance
),
1505 url_loader_(url_loader
.Pass()),
1506 pexe_url_(pexe_url
),
1507 pexe_opt_level_(pexe_opt_level
),
1508 stream_handler_(stream_handler
),
1509 stream_handler_user_data_(stream_handler_user_data
),
1511 expected_content_length_(-1),
1512 weak_factory_(this) { }
1514 void Load(const blink::WebURLRequest
& request
) {
1515 url_loader_
->loadAsynchronously(request
, this);
1519 virtual void didReceiveResponse(blink::WebURLLoader
* loader
,
1520 const blink::WebURLResponse
& response
) {
1521 success_
= (response
.httpStatusCode() == 200);
1525 expected_content_length_
= response
.expectedContentLength();
1527 // Defer loading after receiving headers. This is because we may already
1528 // have a cached translated nexe, so check for that now.
1529 url_loader_
->setDefersLoading(true);
1531 std::string etag
= response
.httpHeaderField("etag").utf8();
1532 std::string last_modified
=
1533 response
.httpHeaderField("last-modified").utf8();
1534 base::Time last_modified_time
;
1535 base::Time::FromString(last_modified
.c_str(), &last_modified_time
);
1537 bool has_no_store_header
= false;
1538 std::string cache_control
=
1539 response
.httpHeaderField("cache-control").utf8();
1541 std::vector
<std::string
> values
;
1542 base::SplitString(cache_control
, ',', &values
);
1543 for (std::vector
<std::string
>::const_iterator it
= values
.begin();
1546 if (base::StringToLowerASCII(*it
) == "no-store")
1547 has_no_store_header
= true;
1550 GetNexeFd(instance_
,
1555 has_no_store_header
,
1556 base::Bind(&PexeDownloader::didGetNexeFd
,
1557 weak_factory_
.GetWeakPtr()));
1560 virtual void didGetNexeFd(int32_t pp_error
,
1562 PP_FileHandle file_handle
) {
1563 if (!content::PepperPluginInstance::Get(instance_
)) {
1568 HistogramEnumerate("NaCl.Perf.PNaClCache.IsHit", cache_hit
, 2);
1570 stream_handler_
->DidCacheHit(stream_handler_user_data_
, file_handle
);
1572 // We delete the PexeDownloader at this point since we successfully got a
1573 // cached, translated nexe.
1577 stream_handler_
->DidCacheMiss(stream_handler_user_data_
,
1578 expected_content_length_
,
1581 // No translated nexe was found in the cache, so we should download the
1582 // file to start streaming it.
1583 url_loader_
->setDefersLoading(false);
1586 virtual void didReceiveData(blink::WebURLLoader
* loader
,
1589 int encoded_data_length
) {
1590 if (content::PepperPluginInstance::Get(instance_
)) {
1591 // Stream the data we received to the stream callback.
1592 stream_handler_
->DidStreamData(stream_handler_user_data_
,
1598 virtual void didFinishLoading(blink::WebURLLoader
* loader
,
1600 int64_t total_encoded_data_length
) {
1601 int32_t result
= success_
? PP_OK
: PP_ERROR_FAILED
;
1603 if (content::PepperPluginInstance::Get(instance_
))
1604 stream_handler_
->DidFinishStream(stream_handler_user_data_
, result
);
1608 virtual void didFail(blink::WebURLLoader
* loader
,
1609 const blink::WebURLError
& error
) {
1613 PP_Instance instance_
;
1614 scoped_ptr
<blink::WebURLLoader
> url_loader_
;
1615 std::string pexe_url_
;
1616 int32_t pexe_opt_level_
;
1617 const PPP_PexeStreamHandler
* stream_handler_
;
1618 void* stream_handler_user_data_
;
1620 int64_t expected_content_length_
;
1621 base::WeakPtrFactory
<PexeDownloader
> weak_factory_
;
1624 void StreamPexe(PP_Instance instance
,
1625 const char* pexe_url
,
1627 const PPP_PexeStreamHandler
* handler
,
1628 void* handler_user_data
) {
1629 content::PepperPluginInstance
* plugin_instance
=
1630 content::PepperPluginInstance::Get(instance
);
1631 if (!plugin_instance
) {
1632 base::MessageLoop::current()->PostTask(
1634 base::Bind(handler
->DidFinishStream
,
1636 static_cast<int32_t>(PP_ERROR_FAILED
)));
1640 GURL
gurl(pexe_url
);
1641 const blink::WebDocument
& document
=
1642 plugin_instance
->GetContainer()->element().document();
1643 scoped_ptr
<blink::WebURLLoader
> url_loader(
1644 CreateWebURLLoader(document
, gurl
));
1645 PexeDownloader
* downloader
= new PexeDownloader(instance
,
1652 blink::WebURLRequest url_request
= CreateWebURLRequest(document
, gurl
);
1653 // Mark the request as requesting a PNaCl bitcode file,
1654 // so that component updater can detect this user action.
1655 url_request
.addHTTPHeaderField(
1656 blink::WebString::fromUTF8("Accept"),
1657 blink::WebString::fromUTF8("application/x-pnacl, */*"));
1658 url_request
.setRequestContext(blink::WebURLRequest::RequestContextObject
);
1659 downloader
->Load(url_request
);
1662 const PPB_NaCl_Private nacl_interface
= {
1666 &Are3DInterfacesDisabled
,
1667 &BrokerDuplicateHandle
,
1668 &GetReadExecPnaclFd
,
1669 &CreateTemporaryFile
,
1670 &GetNumberOfProcessors
,
1671 &PPIsNonSFIModeEnabled
,
1672 &ReportTranslationFinished
,
1679 &NaClDebugEnabledForURL
,
1686 &RequestNaClManifest
,
1687 &GetManifestBaseURL
,
1688 &ProcessNaClManifest
,
1689 &DevInterfacesEnabled
,
1690 &ManifestGetProgramURL
,
1691 &GetPNaClResourceInfo
,
1692 &GetCpuFeatureAttrs
,
1694 &ReportSelLdrStatus
,
1703 const PPB_NaCl_Private
* GetNaClPrivateInterface() {
1704 return &nacl_interface
;