1 // Copyright 2013 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
5 #include "components/nacl/renderer/ppb_nacl_private_impl.h"
11 #include "base/bind.h"
12 #include "base/bind_helpers.h"
13 #include "base/command_line.h"
14 #include "base/containers/scoped_ptr_hash_map.h"
16 #include "base/files/file.h"
17 #include "base/lazy_instance.h"
18 #include "base/logging.h"
19 #include "base/rand_util.h"
20 #include "base/strings/string_split.h"
21 #include "base/strings/string_util.h"
22 #include "components/nacl/common/nacl_host_messages.h"
23 #include "components/nacl/common/nacl_messages.h"
24 #include "components/nacl/common/nacl_nonsfi_util.h"
25 #include "components/nacl/common/nacl_switches.h"
26 #include "components/nacl/common/nacl_types.h"
27 #include "components/nacl/renderer/file_downloader.h"
28 #include "components/nacl/renderer/histogram.h"
29 #include "components/nacl/renderer/json_manifest.h"
30 #include "components/nacl/renderer/manifest_downloader.h"
31 #include "components/nacl/renderer/manifest_service_channel.h"
32 #include "components/nacl/renderer/nexe_load_manager.h"
33 #include "components/nacl/renderer/platform_info.h"
34 #include "components/nacl/renderer/pnacl_translation_resource_host.h"
35 #include "components/nacl/renderer/progress_event.h"
36 #include "components/nacl/renderer/trusted_plugin_channel.h"
37 #include "content/public/common/content_client.h"
38 #include "content/public/common/content_switches.h"
39 #include "content/public/common/sandbox_init.h"
40 #include "content/public/renderer/pepper_plugin_instance.h"
41 #include "content/public/renderer/render_thread.h"
42 #include "content/public/renderer/render_view.h"
43 #include "content/public/renderer/renderer_ppapi_host.h"
44 #include "native_client/src/public/imc_types.h"
45 #include "net/base/data_url.h"
46 #include "net/base/net_errors.h"
47 #include "net/http/http_util.h"
48 #include "ppapi/c/pp_bool.h"
49 #include "ppapi/c/private/pp_file_handle.h"
50 #include "ppapi/shared_impl/ppapi_globals.h"
51 #include "ppapi/shared_impl/ppapi_permissions.h"
52 #include "ppapi/shared_impl/ppapi_preferences.h"
53 #include "ppapi/shared_impl/var.h"
54 #include "ppapi/shared_impl/var_tracker.h"
55 #include "ppapi/thunk/enter.h"
56 #include "third_party/WebKit/public/platform/WebURLLoader.h"
57 #include "third_party/WebKit/public/platform/WebURLResponse.h"
58 #include "third_party/WebKit/public/web/WebDocument.h"
59 #include "third_party/WebKit/public/web/WebElement.h"
60 #include "third_party/WebKit/public/web/WebLocalFrame.h"
61 #include "third_party/WebKit/public/web/WebPluginContainer.h"
62 #include "third_party/WebKit/public/web/WebSecurityOrigin.h"
63 #include "third_party/WebKit/public/web/WebURLLoaderOptions.h"
64 #include "third_party/jsoncpp/source/include/json/reader.h"
65 #include "third_party/jsoncpp/source/include/json/value.h"
70 // The pseudo-architecture used to indicate portable native client.
71 const char* const kPortableArch
= "portable";
73 // The base URL for resources used by the PNaCl translator processes.
74 const char* kPNaClTranslatorBaseUrl
= "chrome://pnacl-translator/";
76 base::LazyInstance
<scoped_refptr
<PnaclTranslationResourceHost
> >
77 g_pnacl_resource_host
= LAZY_INSTANCE_INITIALIZER
;
79 bool InitializePnaclResourceHost() {
80 // Must run on the main thread.
81 content::RenderThread
* render_thread
= content::RenderThread::Get();
84 if (!g_pnacl_resource_host
.Get().get()) {
85 g_pnacl_resource_host
.Get() = new PnaclTranslationResourceHost(
86 render_thread
->GetIOMessageLoopProxy());
87 render_thread
->AddFilter(g_pnacl_resource_host
.Get().get());
92 // This contains state that is produced by LaunchSelLdr() and consumed
93 // by StartPpapiProxy().
95 InstanceInfo() : plugin_pid(base::kNullProcessId
), plugin_child_id(0) {}
97 ppapi::PpapiPermissions permissions
;
98 base::ProcessId plugin_pid
;
100 IPC::ChannelHandle channel_handle
;
103 class NaClPluginInstance
{
105 NaClPluginInstance(PP_Instance instance
):
106 nexe_load_manager(instance
), pexe_size(0) {}
108 NexeLoadManager nexe_load_manager
;
109 scoped_ptr
<JsonManifest
> json_manifest
;
110 scoped_ptr
<InstanceInfo
> instance_info
;
112 // When translation is complete, this records the size of the pexe in
113 // bytes so that it can be reported in a later load event.
117 typedef base::ScopedPtrHashMap
<PP_Instance
, NaClPluginInstance
> InstanceMap
;
118 base::LazyInstance
<InstanceMap
> g_instance_map
= LAZY_INSTANCE_INITIALIZER
;
120 NaClPluginInstance
* GetNaClPluginInstance(PP_Instance instance
) {
121 InstanceMap
& map
= g_instance_map
.Get();
122 InstanceMap::iterator iter
= map
.find(instance
);
123 if (iter
== map
.end())
128 NexeLoadManager
* GetNexeLoadManager(PP_Instance instance
) {
129 NaClPluginInstance
* nacl_plugin_instance
= GetNaClPluginInstance(instance
);
130 if (!nacl_plugin_instance
)
132 return &nacl_plugin_instance
->nexe_load_manager
;
135 JsonManifest
* GetJsonManifest(PP_Instance instance
) {
136 NaClPluginInstance
* nacl_plugin_instance
= GetNaClPluginInstance(instance
);
137 if (!nacl_plugin_instance
)
139 return nacl_plugin_instance
->json_manifest
.get();
142 static const PP_NaClFileInfo kInvalidNaClFileInfo
= {
143 PP_kInvalidFileHandle
,
148 int GetRoutingID(PP_Instance instance
) {
149 // Check that we are on the main renderer thread.
150 DCHECK(content::RenderThread::Get());
151 content::RendererPpapiHost
* host
=
152 content::RendererPpapiHost::GetForPPInstance(instance
);
155 return host
->GetRoutingIDForWidget(instance
);
158 // Returns whether the channel_handle is valid or not.
159 bool IsValidChannelHandle(const IPC::ChannelHandle
& channel_handle
) {
160 if (channel_handle
.name
.empty()) {
164 #if defined(OS_POSIX)
165 if (channel_handle
.socket
.fd
== -1) {
173 void PostPPCompletionCallback(PP_CompletionCallback callback
,
175 ppapi::PpapiGlobals::Get()->GetMainThreadMessageLoop()->PostTask(
177 base::Bind(callback
.func
, callback
.user_data
, status
));
180 bool ManifestResolveKey(PP_Instance instance
,
181 bool is_helper_process
,
182 const std::string
& key
,
183 std::string
* full_url
,
184 PP_PNaClOptions
* pnacl_options
);
186 typedef base::Callback
<void(int32_t, const PP_NaClFileInfo
&)>
187 DownloadFileCallback
;
189 void DownloadFile(PP_Instance instance
,
190 const std::string
& url
,
191 const DownloadFileCallback
& callback
);
193 PP_Bool
StartPpapiProxy(PP_Instance instance
);
195 // Thin adapter from PPP_ManifestService to ManifestServiceChannel::Delegate.
196 // Note that user_data is managed by the caller of LaunchSelLdr. Please see
197 // also PP_ManifestService's comment for more details about resource
199 class ManifestServiceProxy
: public ManifestServiceChannel::Delegate
{
201 ManifestServiceProxy(PP_Instance pp_instance
, NaClAppProcessType process_type
)
202 : pp_instance_(pp_instance
), process_type_(process_type
) {}
204 ~ManifestServiceProxy() override
{}
206 void StartupInitializationComplete() override
{
207 if (StartPpapiProxy(pp_instance_
) == PP_TRUE
) {
208 NaClPluginInstance
* nacl_plugin_instance
=
209 GetNaClPluginInstance(pp_instance_
);
210 JsonManifest
* manifest
= GetJsonManifest(pp_instance_
);
211 if (nacl_plugin_instance
&& manifest
) {
212 NexeLoadManager
* load_manager
=
213 &nacl_plugin_instance
->nexe_load_manager
;
214 std::string full_url
;
215 PP_PNaClOptions pnacl_options
;
216 bool uses_nonsfi_mode
;
217 JsonManifest::ErrorInfo error_info
;
218 if (manifest
->GetProgramURL(&full_url
,
222 int64_t exe_size
= nacl_plugin_instance
->pexe_size
;
224 exe_size
= load_manager
->nexe_size();
225 load_manager
->ReportLoadSuccess(full_url
, exe_size
, exe_size
);
232 const std::string
& key
,
233 const ManifestServiceChannel::OpenResourceCallback
& callback
) override
{
234 DCHECK(ppapi::PpapiGlobals::Get()->GetMainThreadMessageLoop()->
235 BelongsToCurrentThread());
237 // For security hardening, disable open_resource() when it is isn't
238 // needed. PNaCl pexes can't use open_resource(), but general nexes
239 // and the PNaCl translator nexes may use it.
240 if (process_type_
!= kNativeNaClProcessType
&&
241 process_type_
!= kPNaClTranslatorProcessType
) {
243 base::MessageLoop::current()->PostTask(
245 base::Bind(callback
, base::Passed(base::File()), 0, 0));
250 // TODO(teravest): Clean up pnacl_options logic in JsonManifest so we don't
251 // have to initialize it like this here.
252 PP_PNaClOptions pnacl_options
;
253 pnacl_options
.translate
= PP_FALSE
;
254 pnacl_options
.is_debug
= PP_FALSE
;
255 pnacl_options
.opt_level
= 2;
256 bool is_helper_process
= process_type_
== kPNaClTranslatorProcessType
;
257 if (!ManifestResolveKey(pp_instance_
, is_helper_process
, key
, &url
,
259 base::MessageLoop::current()->PostTask(
261 base::Bind(callback
, base::Passed(base::File()), 0, 0));
265 // We have to call DidDownloadFile, even if this object is destroyed, so
266 // that the handle inside PP_NaClFileInfo isn't leaked. This means that the
267 // callback passed to this function shouldn't have a weak pointer to an
270 // TODO(teravest): Make a type like PP_NaClFileInfo to use for DownloadFile
271 // that would close the file handle on destruction.
272 DownloadFile(pp_instance_
, url
,
273 base::Bind(&ManifestServiceProxy::DidDownloadFile
, callback
));
277 static void DidDownloadFile(
278 ManifestServiceChannel::OpenResourceCallback callback
,
280 const PP_NaClFileInfo
& file_info
) {
281 if (pp_error
!= PP_OK
) {
282 callback
.Run(base::File(), 0, 0);
285 callback
.Run(base::File(file_info
.handle
),
290 PP_Instance pp_instance_
;
291 NaClAppProcessType process_type_
;
292 DISALLOW_COPY_AND_ASSIGN(ManifestServiceProxy
);
295 blink::WebURLLoader
* CreateWebURLLoader(const blink::WebDocument
& document
,
297 blink::WebURLLoaderOptions options
;
298 options
.untrustedHTTP
= true;
300 // Options settings here follow the original behavior in the trusted
301 // plugin and PepperURLLoaderHost.
302 if (document
.securityOrigin().canRequest(gurl
)) {
303 options
.allowCredentials
= true;
306 options
.crossOriginRequestPolicy
=
307 blink::WebURLLoaderOptions::CrossOriginRequestPolicyUseAccessControl
;
309 return document
.frame()->createAssociatedURLLoader(options
);
312 blink::WebURLRequest
CreateWebURLRequest(const blink::WebDocument
& document
,
314 blink::WebURLRequest request
;
315 request
.initialize();
316 request
.setURL(gurl
);
317 request
.setFirstPartyForCookies(document
.firstPartyForCookies());
321 int32_t FileDownloaderToPepperError(FileDownloader::Status status
) {
323 case FileDownloader::SUCCESS
:
325 case FileDownloader::ACCESS_DENIED
:
326 return PP_ERROR_NOACCESS
;
327 case FileDownloader::FAILED
:
328 return PP_ERROR_FAILED
;
329 // No default case, to catch unhandled Status values.
331 return PP_ERROR_FAILED
;
334 NaClAppProcessType
PP_ToNaClAppProcessType(
335 PP_NaClAppProcessType pp_process_type
) {
336 #define STATICALLY_CHECK_NACLAPPPROCESSTYPE_EQ(pp, nonpp) \
337 static_assert(static_cast<int>(pp) == static_cast<int>(nonpp), \
338 "PP_NaClAppProcessType differs from NaClAppProcessType");
339 STATICALLY_CHECK_NACLAPPPROCESSTYPE_EQ(PP_UNKNOWN_NACL_PROCESS_TYPE
,
340 kUnknownNaClProcessType
);
341 STATICALLY_CHECK_NACLAPPPROCESSTYPE_EQ(PP_NATIVE_NACL_PROCESS_TYPE
,
342 kNativeNaClProcessType
);
343 STATICALLY_CHECK_NACLAPPPROCESSTYPE_EQ(PP_PNACL_PROCESS_TYPE
,
345 STATICALLY_CHECK_NACLAPPPROCESSTYPE_EQ(PP_PNACL_TRANSLATOR_PROCESS_TYPE
,
346 kPNaClTranslatorProcessType
);
347 STATICALLY_CHECK_NACLAPPPROCESSTYPE_EQ(PP_NUM_NACL_PROCESS_TYPES
,
348 kNumNaClProcessTypes
);
349 #undef STATICALLY_CHECK_NACLAPPPROCESSTYPE_EQ
350 DCHECK(pp_process_type
> PP_UNKNOWN_NACL_PROCESS_TYPE
&&
351 pp_process_type
< PP_NUM_NACL_PROCESS_TYPES
);
352 return static_cast<NaClAppProcessType
>(pp_process_type
);
355 // Launch NaCl's sel_ldr process.
356 void LaunchSelLdr(PP_Instance instance
,
357 PP_Bool main_service_runtime
,
358 const char* alleged_url
,
359 const PP_NaClFileInfo
* nexe_file_info
,
360 PP_Bool uses_nonsfi_mode
,
361 PP_NaClAppProcessType pp_process_type
,
363 PP_CompletionCallback callback
) {
364 CHECK(ppapi::PpapiGlobals::Get()->GetMainThreadMessageLoop()->
365 BelongsToCurrentThread());
366 NaClAppProcessType process_type
= PP_ToNaClAppProcessType(pp_process_type
);
367 // Create the manifest service proxy here, so on error case, it will be
368 // destructed (without passing it to ManifestServiceChannel).
369 scoped_ptr
<ManifestServiceChannel::Delegate
> manifest_service_proxy(
370 new ManifestServiceProxy(instance
, process_type
));
372 FileDescriptor result_socket
;
373 IPC::Sender
* sender
= content::RenderThread::Get();
375 int routing_id
= GetRoutingID(instance
);
376 NexeLoadManager
* load_manager
= GetNexeLoadManager(instance
);
377 DCHECK(load_manager
);
378 if (!routing_id
|| !load_manager
) {
379 if (nexe_file_info
->handle
!= PP_kInvalidFileHandle
) {
380 base::File
closer(nexe_file_info
->handle
);
382 ppapi::PpapiGlobals::Get()->GetMainThreadMessageLoop()->PostTask(
383 FROM_HERE
, base::Bind(callback
.func
, callback
.user_data
,
384 static_cast<int32_t>(PP_ERROR_FAILED
)));
388 InstanceInfo instance_info
;
389 instance_info
.url
= GURL(alleged_url
);
391 uint32_t perm_bits
= ppapi::PERMISSION_NONE
;
392 // Conditionally block 'Dev' interfaces. We do this for the NaCl process, so
393 // it's clearer to developers when they are using 'Dev' inappropriately. We
394 // must also check on the trusted side of the proxy.
395 if (load_manager
->DevInterfacesEnabled())
396 perm_bits
|= ppapi::PERMISSION_DEV
;
397 instance_info
.permissions
=
398 ppapi::PpapiPermissions::GetForCommandLine(perm_bits
);
399 std::string error_message_string
;
400 NaClLaunchResult launch_result
;
402 IPC::PlatformFileForTransit nexe_for_transit
=
403 IPC::InvalidPlatformFileForTransit();
404 #if defined(OS_POSIX)
405 if (nexe_file_info
->handle
!= PP_kInvalidFileHandle
)
406 nexe_for_transit
= base::FileDescriptor(nexe_file_info
->handle
, true);
407 #elif defined(OS_WIN)
408 // Duplicate the handle on the browser side instead of the renderer.
409 // This is because BrokerGetFileForProcess isn't part of content/public, and
410 // it's simpler to do the duplication in the browser anyway.
411 nexe_for_transit
= nexe_file_info
->handle
;
413 #error Unsupported target platform.
415 if (!sender
->Send(new NaClHostMsg_LaunchNaCl(
417 instance_info
.url
.spec(),
419 nexe_file_info
->token_lo
,
420 nexe_file_info
->token_hi
,
423 PP_ToBool(uses_nonsfi_mode
),
426 &error_message_string
))) {
427 ppapi::PpapiGlobals::Get()->GetMainThreadMessageLoop()->PostTask(
429 base::Bind(callback
.func
, callback
.user_data
,
430 static_cast<int32_t>(PP_ERROR_FAILED
)));
434 load_manager
->set_nonsfi(PP_ToBool(uses_nonsfi_mode
));
436 if (!error_message_string
.empty()) {
437 if (PP_ToBool(main_service_runtime
)) {
438 load_manager
->ReportLoadError(PP_NACL_ERROR_SEL_LDR_LAUNCH
,
439 "ServiceRuntime: failed to start",
440 error_message_string
);
442 ppapi::PpapiGlobals::Get()->GetMainThreadMessageLoop()->PostTask(
444 base::Bind(callback
.func
, callback
.user_data
,
445 static_cast<int32_t>(PP_ERROR_FAILED
)));
448 result_socket
= launch_result
.imc_channel_handle
;
449 instance_info
.channel_handle
= launch_result
.ppapi_ipc_channel_handle
;
450 instance_info
.plugin_pid
= launch_result
.plugin_pid
;
451 instance_info
.plugin_child_id
= launch_result
.plugin_child_id
;
453 // Don't save instance_info if channel handle is invalid.
454 if (IsValidChannelHandle(instance_info
.channel_handle
)) {
455 NaClPluginInstance
* nacl_plugin_instance
= GetNaClPluginInstance(instance
);
456 nacl_plugin_instance
->instance_info
.reset(new InstanceInfo(instance_info
));
459 *(static_cast<NaClHandle
*>(imc_handle
)) = ToNativeHandle(result_socket
);
461 // Store the crash information shared memory handle.
462 load_manager
->set_crash_info_shmem_handle(
463 launch_result
.crash_info_shmem_handle
);
465 // Create the trusted plugin channel.
466 if (IsValidChannelHandle(launch_result
.trusted_ipc_channel_handle
)) {
467 bool report_exit_status
= PP_ToBool(main_service_runtime
);
468 scoped_ptr
<TrustedPluginChannel
> trusted_plugin_channel(
469 new TrustedPluginChannel(
471 launch_result
.trusted_ipc_channel_handle
,
472 content::RenderThread::Get()->GetShutdownEvent(),
473 report_exit_status
));
474 load_manager
->set_trusted_plugin_channel(trusted_plugin_channel
.Pass());
476 PostPPCompletionCallback(callback
, PP_ERROR_FAILED
);
480 // Create the manifest service handle as well.
481 if (IsValidChannelHandle(launch_result
.manifest_service_ipc_channel_handle
)) {
482 scoped_ptr
<ManifestServiceChannel
> manifest_service_channel(
483 new ManifestServiceChannel(
484 launch_result
.manifest_service_ipc_channel_handle
,
485 base::Bind(&PostPPCompletionCallback
, callback
),
486 manifest_service_proxy
.Pass(),
487 content::RenderThread::Get()->GetShutdownEvent()));
488 load_manager
->set_manifest_service_channel(
489 manifest_service_channel
.Pass());
493 PP_Bool
StartPpapiProxy(PP_Instance instance
) {
494 NexeLoadManager
* load_manager
= GetNexeLoadManager(instance
);
495 DCHECK(load_manager
);
499 content::PepperPluginInstance
* plugin_instance
=
500 content::PepperPluginInstance::Get(instance
);
501 if (!plugin_instance
) {
502 DLOG(ERROR
) << "GetInstance() failed";
506 NaClPluginInstance
* nacl_plugin_instance
= GetNaClPluginInstance(instance
);
507 if (!nacl_plugin_instance
->instance_info
) {
508 DLOG(ERROR
) << "Could not find instance ID";
511 scoped_ptr
<InstanceInfo
> instance_info
=
512 nacl_plugin_instance
->instance_info
.Pass();
514 PP_ExternalPluginResult result
= plugin_instance
->SwitchToOutOfProcessProxy(
515 base::FilePath().AppendASCII(instance_info
->url
.spec()),
516 instance_info
->permissions
,
517 instance_info
->channel_handle
,
518 instance_info
->plugin_pid
,
519 instance_info
->plugin_child_id
);
521 if (result
== PP_EXTERNAL_PLUGIN_OK
) {
522 // Log the amound of time that has passed between the trusted plugin being
523 // initialized and the untrusted plugin being initialized. This is
524 // (roughly) the cost of using NaCl, in terms of startup time.
525 load_manager
->ReportStartupOverhead();
527 } else if (result
== PP_EXTERNAL_PLUGIN_ERROR_MODULE
) {
528 load_manager
->ReportLoadError(PP_NACL_ERROR_START_PROXY_MODULE
,
529 "could not initialize module.");
530 } else if (result
== PP_EXTERNAL_PLUGIN_ERROR_INSTANCE
) {
531 load_manager
->ReportLoadError(PP_NACL_ERROR_START_PROXY_MODULE
,
532 "could not create instance.");
537 int UrandomFD(void) {
538 #if defined(OS_POSIX)
539 return base::GetUrandomFD();
545 int32_t BrokerDuplicateHandle(PP_FileHandle source_handle
,
547 PP_FileHandle
* target_handle
,
548 uint32_t desired_access
,
551 return content::BrokerDuplicateHandle(source_handle
, process_id
,
552 target_handle
, desired_access
,
559 // Convert a URL to a filename for GetReadonlyPnaclFd.
560 // Must be kept in sync with PnaclCanOpenFile() in
561 // components/nacl/browser/nacl_file_host.cc.
562 std::string
PnaclComponentURLToFilename(const std::string
& url
) {
563 // PNaCl component URLs aren't arbitrary URLs; they are always either
564 // generated from ManifestResolveKey or PnaclResources::ReadResourceInfo.
565 // So, it's safe to just use string parsing operations here instead of
567 DCHECK(StartsWithASCII(url
, kPNaClTranslatorBaseUrl
, true));
568 std::string r
= url
.substr(std::string(kPNaClTranslatorBaseUrl
).length());
570 // Use white-listed-chars.
572 static const char* white_list
= "abcdefghijklmnopqrstuvwxyz0123456789_";
573 replace_pos
= r
.find_first_not_of(white_list
);
574 while(replace_pos
!= std::string::npos
) {
575 r
= r
.replace(replace_pos
, 1, "_");
576 replace_pos
= r
.find_first_not_of(white_list
);
581 PP_FileHandle
GetReadonlyPnaclFd(const char* url
,
584 uint64_t* nonce_hi
) {
585 std::string filename
= PnaclComponentURLToFilename(url
);
586 IPC::PlatformFileForTransit out_fd
= IPC::InvalidPlatformFileForTransit();
587 IPC::Sender
* sender
= content::RenderThread::Get();
589 if (!sender
->Send(new NaClHostMsg_GetReadonlyPnaclFD(
590 std::string(filename
), is_executable
,
591 &out_fd
, nonce_lo
, nonce_hi
))) {
592 return PP_kInvalidFileHandle
;
594 if (out_fd
== IPC::InvalidPlatformFileForTransit()) {
595 return PP_kInvalidFileHandle
;
597 return IPC::PlatformFileForTransitToPlatformFile(out_fd
);
600 void GetReadExecPnaclFd(const char* url
,
601 PP_NaClFileInfo
* out_file_info
) {
602 *out_file_info
= kInvalidNaClFileInfo
;
603 out_file_info
->handle
= GetReadonlyPnaclFd(url
, true /* is_executable */,
604 &out_file_info
->token_lo
,
605 &out_file_info
->token_hi
);
608 PP_FileHandle
CreateTemporaryFile(PP_Instance instance
) {
609 IPC::PlatformFileForTransit transit_fd
= IPC::InvalidPlatformFileForTransit();
610 IPC::Sender
* sender
= content::RenderThread::Get();
612 if (!sender
->Send(new NaClHostMsg_NaClCreateTemporaryFile(
614 return PP_kInvalidFileHandle
;
617 if (transit_fd
== IPC::InvalidPlatformFileForTransit()) {
618 return PP_kInvalidFileHandle
;
621 return IPC::PlatformFileForTransitToPlatformFile(transit_fd
);
624 int32_t GetNumberOfProcessors() {
625 IPC::Sender
* sender
= content::RenderThread::Get();
627 int32_t num_processors
= 1;
628 return sender
->Send(new NaClHostMsg_NaClGetNumProcessors(&num_processors
)) ?
632 void GetNexeFd(PP_Instance instance
,
633 const std::string
& pexe_url
,
635 const base::Time
& last_modified_time
,
636 const std::string
& etag
,
637 bool has_no_store_header
,
638 base::Callback
<void(int32_t, bool, PP_FileHandle
)> callback
) {
639 if (!InitializePnaclResourceHost()) {
640 ppapi::PpapiGlobals::Get()->GetMainThreadMessageLoop()->PostTask(
643 static_cast<int32_t>(PP_ERROR_FAILED
),
645 PP_kInvalidFileHandle
));
649 PnaclCacheInfo cache_info
;
650 cache_info
.pexe_url
= GURL(pexe_url
);
651 // TODO(dschuff): Get this value from the pnacl json file after it
652 // rolls in from NaCl.
653 cache_info
.abi_version
= 1;
654 cache_info
.opt_level
= opt_level
;
655 cache_info
.last_modified
= last_modified_time
;
656 cache_info
.etag
= etag
;
657 cache_info
.has_no_store_header
= has_no_store_header
;
658 cache_info
.sandbox_isa
= GetSandboxArch();
659 cache_info
.extra_flags
= GetCpuFeatures();
661 g_pnacl_resource_host
.Get()->RequestNexeFd(
662 GetRoutingID(instance
),
668 void ReportTranslationFinished(PP_Instance instance
,
672 int64_t compile_time_us
) {
673 if (success
== PP_TRUE
) {
674 static const int32_t kUnknownOptLevel
= 4;
675 if (opt_level
< 0 || opt_level
> 3)
676 opt_level
= kUnknownOptLevel
;
677 HistogramEnumerate("NaCl.Options.PNaCl.OptLevel",
679 kUnknownOptLevel
+ 1);
680 HistogramKBPerSec("NaCl.Perf.PNaClLoadTime.CompileKBPerSec",
683 HistogramSizeKB("NaCl.Perf.Size.Pexe", pexe_size
/ 1024);
685 NexeLoadManager
* load_manager
= GetNexeLoadManager(instance
);
687 base::TimeDelta total_time
= base::Time::Now() -
688 load_manager
->pnacl_start_time();
689 HistogramTimeTranslation("NaCl.Perf.PNaClLoadTime.TotalUncachedTime",
690 total_time
.InMilliseconds());
691 HistogramKBPerSec("NaCl.Perf.PNaClLoadTime.TotalUncachedKBPerSec",
693 total_time
.InMicroseconds());
697 // If the resource host isn't initialized, don't try to do that here.
698 // Just return because something is already very wrong.
699 if (g_pnacl_resource_host
.Get().get() == NULL
)
701 g_pnacl_resource_host
.Get()->ReportTranslationFinished(instance
, success
);
703 // Record the pexe size for reporting in a later load event.
704 NaClPluginInstance
* nacl_plugin_instance
= GetNaClPluginInstance(instance
);
705 if (nacl_plugin_instance
) {
706 nacl_plugin_instance
->pexe_size
= pexe_size
;
710 PP_FileHandle
OpenNaClExecutable(PP_Instance instance
,
711 const char* file_url
,
713 uint64_t* nonce_hi
) {
714 // Fast path only works for installed file URLs.
716 if (!gurl
.SchemeIs("chrome-extension"))
717 return PP_kInvalidFileHandle
;
719 NexeLoadManager
* load_manager
= GetNexeLoadManager(instance
);
720 DCHECK(load_manager
);
722 return PP_kInvalidFileHandle
;
724 content::PepperPluginInstance
* plugin_instance
=
725 content::PepperPluginInstance::Get(instance
);
726 if (!plugin_instance
)
727 return PP_kInvalidFileHandle
;
728 // IMPORTANT: Make sure the document can request the given URL. If we don't
729 // check, a malicious app could probe the extension system. This enforces a
730 // same-origin policy which prevents the app from requesting resources from
732 blink::WebSecurityOrigin security_origin
=
733 plugin_instance
->GetContainer()->element().document().securityOrigin();
734 if (!security_origin
.canRequest(gurl
))
735 return PP_kInvalidFileHandle
;
737 IPC::PlatformFileForTransit out_fd
= IPC::InvalidPlatformFileForTransit();
738 IPC::Sender
* sender
= content::RenderThread::Get();
742 base::FilePath file_path
;
744 new NaClHostMsg_OpenNaClExecutable(GetRoutingID(instance
),
746 !load_manager
->nonsfi(),
750 return PP_kInvalidFileHandle
;
753 if (out_fd
== IPC::InvalidPlatformFileForTransit())
754 return PP_kInvalidFileHandle
;
756 return IPC::PlatformFileForTransitToPlatformFile(out_fd
);
759 void DispatchEvent(PP_Instance instance
,
760 PP_NaClEventType event_type
,
761 const char* resource_url
,
762 PP_Bool length_is_computable
,
763 uint64_t loaded_bytes
,
764 uint64_t total_bytes
) {
765 ProgressEvent
event(event_type
,
767 PP_ToBool(length_is_computable
),
770 DispatchProgressEvent(instance
, event
);
773 void ReportLoadError(PP_Instance instance
,
775 const char* error_message
) {
776 NexeLoadManager
* load_manager
= GetNexeLoadManager(instance
);
778 load_manager
->ReportLoadError(error
, error_message
);
781 void InstanceCreated(PP_Instance instance
) {
782 InstanceMap
& map
= g_instance_map
.Get();
783 CHECK(map
.find(instance
) == map
.end()); // Sanity check.
784 scoped_ptr
<NaClPluginInstance
> new_instance(new NaClPluginInstance(instance
));
785 map
.add(instance
, new_instance
.Pass());
788 void InstanceDestroyed(PP_Instance instance
) {
789 InstanceMap
& map
= g_instance_map
.Get();
790 InstanceMap::iterator iter
= map
.find(instance
);
791 CHECK(iter
!= map
.end());
792 // The erase may call NexeLoadManager's destructor prior to removing it from
793 // the map. In that case, it is possible for the trusted Plugin to re-enter
794 // the NexeLoadManager (e.g., by calling ReportLoadError). Passing out the
795 // NexeLoadManager to a local scoped_ptr just ensures that its entry is gone
796 // from the map prior to the destructor being invoked.
797 scoped_ptr
<NaClPluginInstance
> temp(map
.take(instance
));
801 PP_Bool
NaClDebugEnabledForURL(const char* alleged_nmf_url
) {
802 if (!base::CommandLine::ForCurrentProcess()->HasSwitch(
803 switches::kEnableNaClDebug
))
805 IPC::Sender
* sender
= content::RenderThread::Get();
807 bool should_debug
= false;
809 sender
->Send(new NaClHostMsg_NaClDebugEnabledForURL(GURL(alleged_nmf_url
),
814 void Vlog(const char* message
) {
818 void InitializePlugin(PP_Instance instance
,
821 const char* argv
[]) {
822 NexeLoadManager
* load_manager
= GetNexeLoadManager(instance
);
823 DCHECK(load_manager
);
825 load_manager
->InitializePlugin(argc
, argn
, argv
);
828 void DownloadManifestToBuffer(PP_Instance instance
,
829 struct PP_CompletionCallback callback
);
831 bool CreateJsonManifest(PP_Instance instance
,
832 const std::string
& manifest_url
,
833 const std::string
& manifest_data
);
835 void RequestNaClManifest(PP_Instance instance
,
836 PP_CompletionCallback callback
) {
837 NexeLoadManager
* load_manager
= GetNexeLoadManager(instance
);
838 DCHECK(load_manager
);
840 ppapi::PpapiGlobals::Get()->GetMainThreadMessageLoop()->PostTask(
842 base::Bind(callback
.func
, callback
.user_data
,
843 static_cast<int32_t>(PP_ERROR_FAILED
)));
847 std::string url
= load_manager
->GetManifestURLArgument();
848 if (url
.empty() || !load_manager
->RequestNaClManifest(url
)) {
849 ppapi::PpapiGlobals::Get()->GetMainThreadMessageLoop()->PostTask(
851 base::Bind(callback
.func
, callback
.user_data
,
852 static_cast<int32_t>(PP_ERROR_FAILED
)));
856 const GURL
& base_url
= load_manager
->manifest_base_url();
857 if (base_url
.SchemeIs("data")) {
859 std::string mime_type
;
862 int32_t error
= PP_ERROR_FAILED
;
863 if (net::DataURL::Parse(gurl
, &mime_type
, &charset
, &data
)) {
864 if (data
.size() <= ManifestDownloader::kNaClManifestMaxFileBytes
) {
865 if (CreateJsonManifest(instance
, base_url
.spec(), data
))
868 load_manager
->ReportLoadError(PP_NACL_ERROR_MANIFEST_TOO_LARGE
,
869 "manifest file too large.");
872 load_manager
->ReportLoadError(PP_NACL_ERROR_MANIFEST_LOAD_URL
,
873 "could not load manifest url.");
875 ppapi::PpapiGlobals::Get()->GetMainThreadMessageLoop()->PostTask(
877 base::Bind(callback
.func
, callback
.user_data
, error
));
879 DownloadManifestToBuffer(instance
, callback
);
883 PP_Var
GetManifestBaseURL(PP_Instance instance
) {
884 NexeLoadManager
* load_manager
= GetNexeLoadManager(instance
);
885 DCHECK(load_manager
);
887 return PP_MakeUndefined();
888 const GURL
& gurl
= load_manager
->manifest_base_url();
889 if (!gurl
.is_valid())
890 return PP_MakeUndefined();
891 return ppapi::StringVar::StringToPPVar(gurl
.spec());
894 void ProcessNaClManifest(PP_Instance instance
, const char* program_url
) {
895 nacl::NexeLoadManager
* load_manager
= GetNexeLoadManager(instance
);
897 load_manager
->ProcessNaClManifest(program_url
);
900 void DownloadManifestToBufferCompletion(PP_Instance instance
,
901 struct PP_CompletionCallback callback
,
902 base::Time start_time
,
903 PP_NaClError pp_nacl_error
,
904 const std::string
& data
);
906 void DownloadManifestToBuffer(PP_Instance instance
,
907 struct PP_CompletionCallback callback
) {
908 nacl::NexeLoadManager
* load_manager
= GetNexeLoadManager(instance
);
909 DCHECK(load_manager
);
910 content::PepperPluginInstance
* plugin_instance
=
911 content::PepperPluginInstance::Get(instance
);
912 if (!load_manager
|| !plugin_instance
) {
913 ppapi::PpapiGlobals::Get()->GetMainThreadMessageLoop()->PostTask(
915 base::Bind(callback
.func
, callback
.user_data
,
916 static_cast<int32_t>(PP_ERROR_FAILED
)));
918 const blink::WebDocument
& document
=
919 plugin_instance
->GetContainer()->element().document();
921 const GURL
& gurl
= load_manager
->manifest_base_url();
922 scoped_ptr
<blink::WebURLLoader
> url_loader(
923 CreateWebURLLoader(document
, gurl
));
924 blink::WebURLRequest request
= CreateWebURLRequest(document
, gurl
);
926 // ManifestDownloader deletes itself after invoking the callback.
927 ManifestDownloader
* manifest_downloader
= new ManifestDownloader(
929 load_manager
->is_installed(),
930 base::Bind(DownloadManifestToBufferCompletion
,
931 instance
, callback
, base::Time::Now()));
932 manifest_downloader
->Load(request
);
935 void DownloadManifestToBufferCompletion(PP_Instance instance
,
936 struct PP_CompletionCallback callback
,
937 base::Time start_time
,
938 PP_NaClError pp_nacl_error
,
939 const std::string
& data
) {
940 base::TimeDelta download_time
= base::Time::Now() - start_time
;
941 HistogramTimeSmall("NaCl.Perf.StartupTime.ManifestDownload",
942 download_time
.InMilliseconds());
944 nacl::NexeLoadManager
* load_manager
= GetNexeLoadManager(instance
);
946 callback
.func(callback
.user_data
, PP_ERROR_ABORTED
);
951 switch (pp_nacl_error
) {
952 case PP_NACL_ERROR_LOAD_SUCCESS
:
955 case PP_NACL_ERROR_MANIFEST_LOAD_URL
:
956 pp_error
= PP_ERROR_FAILED
;
957 load_manager
->ReportLoadError(PP_NACL_ERROR_MANIFEST_LOAD_URL
,
958 "could not load manifest url.");
960 case PP_NACL_ERROR_MANIFEST_TOO_LARGE
:
961 pp_error
= PP_ERROR_FILETOOBIG
;
962 load_manager
->ReportLoadError(PP_NACL_ERROR_MANIFEST_TOO_LARGE
,
963 "manifest file too large.");
965 case PP_NACL_ERROR_MANIFEST_NOACCESS_URL
:
966 pp_error
= PP_ERROR_NOACCESS
;
967 load_manager
->ReportLoadError(PP_NACL_ERROR_MANIFEST_NOACCESS_URL
,
968 "access to manifest url was denied.");
972 pp_error
= PP_ERROR_FAILED
;
973 load_manager
->ReportLoadError(PP_NACL_ERROR_MANIFEST_LOAD_URL
,
974 "could not load manifest url.");
977 if (pp_error
== PP_OK
) {
978 std::string base_url
= load_manager
->manifest_base_url().spec();
979 if (!CreateJsonManifest(instance
, base_url
, data
))
980 pp_error
= PP_ERROR_FAILED
;
982 callback
.func(callback
.user_data
, pp_error
);
985 bool CreateJsonManifest(PP_Instance instance
,
986 const std::string
& manifest_url
,
987 const std::string
& manifest_data
) {
988 HistogramSizeKB("NaCl.Perf.Size.Manifest",
989 static_cast<int32_t>(manifest_data
.length() / 1024));
991 nacl::NexeLoadManager
* load_manager
= GetNexeLoadManager(instance
);
995 const char* isa_type
;
996 if (load_manager
->IsPNaCl())
997 isa_type
= kPortableArch
;
999 isa_type
= GetSandboxArch();
1001 scoped_ptr
<nacl::JsonManifest
> j(
1002 new nacl::JsonManifest(
1003 manifest_url
.c_str(),
1005 IsNonSFIModeEnabled(),
1006 PP_ToBool(NaClDebugEnabledForURL(manifest_url
.c_str()))));
1007 JsonManifest::ErrorInfo error_info
;
1008 if (j
->Init(manifest_data
.c_str(), &error_info
)) {
1009 GetNaClPluginInstance(instance
)->json_manifest
.reset(j
.release());
1012 load_manager
->ReportLoadError(error_info
.error
, error_info
.string
);
1016 PP_Bool
ManifestGetProgramURL(PP_Instance instance
,
1017 PP_Var
* pp_full_url
,
1018 PP_PNaClOptions
* pnacl_options
,
1019 PP_Bool
* pp_uses_nonsfi_mode
) {
1020 nacl::NexeLoadManager
* load_manager
= GetNexeLoadManager(instance
);
1022 JsonManifest
* manifest
= GetJsonManifest(instance
);
1023 if (manifest
== NULL
)
1026 bool uses_nonsfi_mode
;
1027 std::string full_url
;
1028 JsonManifest::ErrorInfo error_info
;
1029 if (manifest
->GetProgramURL(&full_url
, pnacl_options
, &uses_nonsfi_mode
,
1031 *pp_full_url
= ppapi::StringVar::StringToPPVar(full_url
);
1032 *pp_uses_nonsfi_mode
= PP_FromBool(uses_nonsfi_mode
);
1037 load_manager
->ReportLoadError(error_info
.error
, error_info
.string
);
1041 bool ManifestResolveKey(PP_Instance instance
,
1042 bool is_helper_process
,
1043 const std::string
& key
,
1044 std::string
* full_url
,
1045 PP_PNaClOptions
* pnacl_options
) {
1046 // For "helper" processes (llc and ld, for PNaCl translation), we resolve
1047 // keys manually as there is no existing .nmf file to parse.
1048 if (is_helper_process
) {
1049 pnacl_options
->translate
= PP_FALSE
;
1050 // We can only resolve keys in the files/ namespace.
1051 const std::string kFilesPrefix
= "files/";
1052 if (key
.find(kFilesPrefix
) == std::string::npos
) {
1053 nacl::NexeLoadManager
* load_manager
= GetNexeLoadManager(instance
);
1055 load_manager
->ReportLoadError(PP_NACL_ERROR_MANIFEST_RESOLVE_URL
,
1056 "key did not start with files/");
1059 std::string key_basename
= key
.substr(kFilesPrefix
.length());
1060 *full_url
= std::string(kPNaClTranslatorBaseUrl
) + GetSandboxArch() + "/" +
1065 JsonManifest
* manifest
= GetJsonManifest(instance
);
1066 if (manifest
== NULL
)
1069 return manifest
->ResolveKey(key
, full_url
, pnacl_options
);
1072 PP_Bool
GetPNaClResourceInfo(PP_Instance instance
,
1073 PP_Var
* llc_tool_name
,
1074 PP_Var
* ld_tool_name
) {
1075 static const char kFilename
[] = "chrome://pnacl-translator/pnacl.json";
1076 NexeLoadManager
* load_manager
= GetNexeLoadManager(instance
);
1077 DCHECK(load_manager
);
1081 uint64_t nonce_lo
= 0;
1082 uint64_t nonce_hi
= 0;
1083 base::File
file(GetReadonlyPnaclFd(kFilename
, false /* is_executable */,
1084 &nonce_lo
, &nonce_hi
));
1085 if (!file
.IsValid()) {
1086 load_manager
->ReportLoadError(
1087 PP_NACL_ERROR_PNACL_RESOURCE_FETCH
,
1088 "The Portable Native Client (pnacl) component is not "
1089 "installed. Please consult chrome://components for more "
1094 base::File::Info file_info
;
1095 if (!file
.GetInfo(&file_info
)) {
1096 load_manager
->ReportLoadError(
1097 PP_NACL_ERROR_PNACL_RESOURCE_FETCH
,
1098 std::string("GetPNaClResourceInfo, GetFileInfo failed for: ") +
1103 if (file_info
.size
> 1 << 20) {
1104 load_manager
->ReportLoadError(
1105 PP_NACL_ERROR_PNACL_RESOURCE_FETCH
,
1106 std::string("GetPNaClResourceInfo, file too large: ") + kFilename
);
1110 scoped_ptr
<char[]> buffer(new char[file_info
.size
+ 1]);
1111 if (buffer
.get() == NULL
) {
1112 load_manager
->ReportLoadError(
1113 PP_NACL_ERROR_PNACL_RESOURCE_FETCH
,
1114 std::string("GetPNaClResourceInfo, couldn't allocate for: ") +
1119 int rc
= file
.Read(0, buffer
.get(), file_info
.size
);
1121 load_manager
->ReportLoadError(
1122 PP_NACL_ERROR_PNACL_RESOURCE_FETCH
,
1123 std::string("GetPNaClResourceInfo, reading failed for: ") + kFilename
);
1127 // Null-terminate the bytes we we read from the file.
1128 buffer
.get()[rc
] = 0;
1130 // Expect the JSON file to contain a top-level object (dictionary).
1131 Json::Reader json_reader
;
1132 Json::Value json_data
;
1133 if (!json_reader
.parse(buffer
.get(), json_data
)) {
1134 load_manager
->ReportLoadError(
1135 PP_NACL_ERROR_PNACL_RESOURCE_FETCH
,
1136 std::string("Parsing resource info failed: JSON parse error: ") +
1137 json_reader
.getFormattedErrorMessages());
1141 if (!json_data
.isObject()) {
1142 load_manager
->ReportLoadError(
1143 PP_NACL_ERROR_PNACL_RESOURCE_FETCH
,
1144 "Parsing resource info failed: Malformed JSON dictionary");
1148 if (json_data
.isMember("pnacl-llc-name")) {
1149 Json::Value json_name
= json_data
["pnacl-llc-name"];
1150 if (json_name
.isString()) {
1151 std::string llc_tool_name_str
= json_name
.asString();
1152 *llc_tool_name
= ppapi::StringVar::StringToPPVar(llc_tool_name_str
);
1156 if (json_data
.isMember("pnacl-ld-name")) {
1157 Json::Value json_name
= json_data
["pnacl-ld-name"];
1158 if (json_name
.isString()) {
1159 std::string ld_tool_name_str
= json_name
.asString();
1160 *ld_tool_name
= ppapi::StringVar::StringToPPVar(ld_tool_name_str
);
1166 PP_Var
GetCpuFeatureAttrs() {
1167 return ppapi::StringVar::StringToPPVar(GetCpuFeatures());
1170 // Encapsulates some of the state for a call to DownloadNexe to prevent
1171 // argument lists from getting too long.
1172 struct DownloadNexeRequest
{
1173 PP_Instance instance
;
1175 PP_CompletionCallback callback
;
1176 base::Time start_time
;
1179 // A utility class to ensure that we don't send progress events more often than
1180 // every 10ms for a given file.
1181 class ProgressEventRateLimiter
{
1183 explicit ProgressEventRateLimiter(PP_Instance instance
)
1184 : instance_(instance
) { }
1186 void ReportProgress(const std::string
& url
,
1187 int64_t total_bytes_received
,
1188 int64_t total_bytes_to_be_received
) {
1189 base::Time now
= base::Time::Now();
1190 if (now
- last_event_
> base::TimeDelta::FromMilliseconds(10)) {
1191 DispatchProgressEvent(instance_
,
1192 ProgressEvent(PP_NACL_EVENT_PROGRESS
,
1194 total_bytes_to_be_received
>= 0,
1195 total_bytes_received
,
1196 total_bytes_to_be_received
));
1202 PP_Instance instance_
;
1203 base::Time last_event_
;
1206 void DownloadNexeCompletion(const DownloadNexeRequest
& request
,
1207 PP_NaClFileInfo
* out_file_info
,
1208 FileDownloader::Status status
,
1209 base::File target_file
,
1212 void DownloadNexe(PP_Instance instance
,
1214 PP_NaClFileInfo
* out_file_info
,
1215 PP_CompletionCallback callback
) {
1217 CHECK(out_file_info
);
1218 DownloadNexeRequest request
;
1219 request
.instance
= instance
;
1221 request
.callback
= callback
;
1222 request
.start_time
= base::Time::Now();
1224 // Try the fast path for retrieving the file first.
1225 PP_FileHandle handle
= OpenNaClExecutable(instance
,
1227 &out_file_info
->token_lo
,
1228 &out_file_info
->token_hi
);
1229 if (handle
!= PP_kInvalidFileHandle
) {
1230 DownloadNexeCompletion(request
,
1232 FileDownloader::SUCCESS
,
1238 // The fast path didn't work, we'll fetch the file using URLLoader and write
1239 // it to local storage.
1240 base::File
target_file(CreateTemporaryFile(instance
));
1243 content::PepperPluginInstance
* plugin_instance
=
1244 content::PepperPluginInstance::Get(instance
);
1245 if (!plugin_instance
) {
1246 ppapi::PpapiGlobals::Get()->GetMainThreadMessageLoop()->PostTask(
1248 base::Bind(callback
.func
, callback
.user_data
,
1249 static_cast<int32_t>(PP_ERROR_FAILED
)));
1251 const blink::WebDocument
& document
=
1252 plugin_instance
->GetContainer()->element().document();
1253 scoped_ptr
<blink::WebURLLoader
> url_loader(
1254 CreateWebURLLoader(document
, gurl
));
1255 blink::WebURLRequest url_request
= CreateWebURLRequest(document
, gurl
);
1257 ProgressEventRateLimiter
* tracker
= new ProgressEventRateLimiter(instance
);
1259 // FileDownloader deletes itself after invoking DownloadNexeCompletion.
1260 FileDownloader
* file_downloader
= new FileDownloader(
1263 base::Bind(&DownloadNexeCompletion
, request
, out_file_info
),
1264 base::Bind(&ProgressEventRateLimiter::ReportProgress
,
1265 base::Owned(tracker
), std::string(url
)));
1266 file_downloader
->Load(url_request
);
1269 void DownloadNexeCompletion(const DownloadNexeRequest
& request
,
1270 PP_NaClFileInfo
* out_file_info
,
1271 FileDownloader::Status status
,
1272 base::File target_file
,
1274 int32_t pp_error
= FileDownloaderToPepperError(status
);
1275 int64_t bytes_read
= -1;
1276 if (pp_error
== PP_OK
&& target_file
.IsValid()) {
1277 base::File::Info info
;
1278 if (target_file
.GetInfo(&info
))
1279 bytes_read
= info
.size
;
1282 if (bytes_read
== -1) {
1283 target_file
.Close();
1284 pp_error
= PP_ERROR_FAILED
;
1287 base::TimeDelta download_time
= base::Time::Now() - request
.start_time
;
1289 NexeLoadManager
* load_manager
= GetNexeLoadManager(request
.instance
);
1291 load_manager
->NexeFileDidOpen(pp_error
,
1299 if (pp_error
== PP_OK
&& target_file
.IsValid())
1300 out_file_info
->handle
= target_file
.TakePlatformFile();
1302 out_file_info
->handle
= PP_kInvalidFileHandle
;
1304 request
.callback
.func(request
.callback
.user_data
, pp_error
);
1307 void DownloadFileCompletion(
1308 const DownloadFileCallback
& callback
,
1309 FileDownloader::Status status
,
1312 int32_t pp_error
= FileDownloaderToPepperError(status
);
1313 PP_NaClFileInfo file_info
;
1314 if (pp_error
== PP_OK
) {
1315 file_info
.handle
= file
.TakePlatformFile();
1316 file_info
.token_lo
= 0;
1317 file_info
.token_hi
= 0;
1319 file_info
= kInvalidNaClFileInfo
;
1322 callback
.Run(pp_error
, file_info
);
1325 void DownloadFile(PP_Instance instance
,
1326 const std::string
& url
,
1327 const DownloadFileCallback
& callback
) {
1328 DCHECK(ppapi::PpapiGlobals::Get()->GetMainThreadMessageLoop()->
1329 BelongsToCurrentThread());
1331 NexeLoadManager
* load_manager
= GetNexeLoadManager(instance
);
1332 DCHECK(load_manager
);
1333 if (!load_manager
) {
1334 base::MessageLoop::current()->PostTask(
1336 base::Bind(callback
,
1337 static_cast<int32_t>(PP_ERROR_FAILED
),
1338 kInvalidNaClFileInfo
));
1342 // Handle special PNaCl support files which are installed on the user's
1344 if (url
.find(kPNaClTranslatorBaseUrl
, 0) == 0) {
1345 PP_NaClFileInfo file_info
= kInvalidNaClFileInfo
;
1346 PP_FileHandle handle
= GetReadonlyPnaclFd(url
.c_str(),
1347 false /* is_executable */,
1348 &file_info
.token_lo
,
1349 &file_info
.token_hi
);
1350 if (handle
== PP_kInvalidFileHandle
) {
1351 base::MessageLoop::current()->PostTask(
1353 base::Bind(callback
,
1354 static_cast<int32_t>(PP_ERROR_FAILED
),
1355 kInvalidNaClFileInfo
));
1358 file_info
.handle
= handle
;
1359 base::MessageLoop::current()->PostTask(
1361 base::Bind(callback
, static_cast<int32_t>(PP_OK
), file_info
));
1365 // We have to ensure that this url resolves relative to the plugin base url
1366 // before downloading it.
1367 const GURL
& test_gurl
= load_manager
->plugin_base_url().Resolve(url
);
1368 if (!test_gurl
.is_valid()) {
1369 base::MessageLoop::current()->PostTask(
1371 base::Bind(callback
,
1372 static_cast<int32_t>(PP_ERROR_FAILED
),
1373 kInvalidNaClFileInfo
));
1377 // Try the fast path for retrieving the file first.
1378 uint64_t file_token_lo
= 0;
1379 uint64_t file_token_hi
= 0;
1380 PP_FileHandle file_handle
= OpenNaClExecutable(instance
,
1384 if (file_handle
!= PP_kInvalidFileHandle
) {
1385 PP_NaClFileInfo file_info
;
1386 file_info
.handle
= file_handle
;
1387 file_info
.token_lo
= file_token_lo
;
1388 file_info
.token_hi
= file_token_hi
;
1389 base::MessageLoop::current()->PostTask(
1391 base::Bind(callback
, static_cast<int32_t>(PP_OK
), file_info
));
1395 // The fast path didn't work, we'll fetch the file using URLLoader and write
1396 // it to local storage.
1397 base::File
target_file(CreateTemporaryFile(instance
));
1400 content::PepperPluginInstance
* plugin_instance
=
1401 content::PepperPluginInstance::Get(instance
);
1402 if (!plugin_instance
) {
1403 base::MessageLoop::current()->PostTask(
1405 base::Bind(callback
,
1406 static_cast<int32_t>(PP_ERROR_FAILED
),
1407 kInvalidNaClFileInfo
));
1409 const blink::WebDocument
& document
=
1410 plugin_instance
->GetContainer()->element().document();
1411 scoped_ptr
<blink::WebURLLoader
> url_loader(
1412 CreateWebURLLoader(document
, gurl
));
1413 blink::WebURLRequest url_request
= CreateWebURLRequest(document
, gurl
);
1415 ProgressEventRateLimiter
* tracker
= new ProgressEventRateLimiter(instance
);
1417 // FileDownloader deletes itself after invoking DownloadNexeCompletion.
1418 FileDownloader
* file_downloader
= new FileDownloader(
1421 base::Bind(&DownloadFileCompletion
, callback
),
1422 base::Bind(&ProgressEventRateLimiter::ReportProgress
,
1423 base::Owned(tracker
), std::string(url
)));
1424 file_downloader
->Load(url_request
);
1427 void ReportSelLdrStatus(PP_Instance instance
,
1428 int32_t load_status
,
1429 int32_t max_status
) {
1430 HistogramEnumerate("NaCl.LoadStatus.SelLdr", load_status
, max_status
);
1431 NexeLoadManager
* load_manager
= GetNexeLoadManager(instance
);
1432 DCHECK(load_manager
);
1436 // Gather data to see if being installed changes load outcomes.
1437 const char* name
= load_manager
->is_installed() ?
1438 "NaCl.LoadStatus.SelLdr.InstalledApp" :
1439 "NaCl.LoadStatus.SelLdr.NotInstalledApp";
1440 HistogramEnumerate(name
, load_status
, max_status
);
1443 void LogTranslateTime(const char* histogram_name
,
1444 int64_t time_in_us
) {
1445 ppapi::PpapiGlobals::Get()->GetMainThreadMessageLoop()->PostTask(
1447 base::Bind(&HistogramTimeTranslation
,
1448 std::string(histogram_name
),
1449 time_in_us
/ 1000));
1452 void SetPNaClStartTime(PP_Instance instance
) {
1453 NexeLoadManager
* load_manager
= GetNexeLoadManager(instance
);
1455 load_manager
->set_pnacl_start_time(base::Time::Now());
1458 // PexeDownloader is responsible for deleting itself when the download
1460 class PexeDownloader
: public blink::WebURLLoaderClient
{
1462 PexeDownloader(PP_Instance instance
,
1463 scoped_ptr
<blink::WebURLLoader
> url_loader
,
1464 const std::string
& pexe_url
,
1465 int32_t pexe_opt_level
,
1466 const PPP_PexeStreamHandler
* stream_handler
,
1467 void* stream_handler_user_data
)
1468 : instance_(instance
),
1469 url_loader_(url_loader
.Pass()),
1470 pexe_url_(pexe_url
),
1471 pexe_opt_level_(pexe_opt_level
),
1472 stream_handler_(stream_handler
),
1473 stream_handler_user_data_(stream_handler_user_data
),
1475 expected_content_length_(-1),
1476 weak_factory_(this) { }
1478 void Load(const blink::WebURLRequest
& request
) {
1479 url_loader_
->loadAsynchronously(request
, this);
1483 virtual void didReceiveResponse(blink::WebURLLoader
* loader
,
1484 const blink::WebURLResponse
& response
) {
1485 success_
= (response
.httpStatusCode() == 200);
1489 expected_content_length_
= response
.expectedContentLength();
1491 // Defer loading after receiving headers. This is because we may already
1492 // have a cached translated nexe, so check for that now.
1493 url_loader_
->setDefersLoading(true);
1495 std::string etag
= response
.httpHeaderField("etag").utf8();
1496 std::string last_modified
=
1497 response
.httpHeaderField("last-modified").utf8();
1498 base::Time last_modified_time
;
1499 base::Time::FromString(last_modified
.c_str(), &last_modified_time
);
1501 bool has_no_store_header
= false;
1502 std::string cache_control
=
1503 response
.httpHeaderField("cache-control").utf8();
1505 std::vector
<std::string
> values
;
1506 base::SplitString(cache_control
, ',', &values
);
1507 for (std::vector
<std::string
>::const_iterator it
= values
.begin();
1510 if (base::StringToLowerASCII(*it
) == "no-store")
1511 has_no_store_header
= true;
1514 GetNexeFd(instance_
,
1519 has_no_store_header
,
1520 base::Bind(&PexeDownloader::didGetNexeFd
,
1521 weak_factory_
.GetWeakPtr()));
1524 virtual void didGetNexeFd(int32_t pp_error
,
1526 PP_FileHandle file_handle
) {
1527 if (!content::PepperPluginInstance::Get(instance_
)) {
1532 HistogramEnumerate("NaCl.Perf.PNaClCache.IsHit", cache_hit
, 2);
1534 stream_handler_
->DidCacheHit(stream_handler_user_data_
, file_handle
);
1536 // We delete the PexeDownloader at this point since we successfully got a
1537 // cached, translated nexe.
1541 stream_handler_
->DidCacheMiss(stream_handler_user_data_
,
1542 expected_content_length_
,
1545 // No translated nexe was found in the cache, so we should download the
1546 // file to start streaming it.
1547 url_loader_
->setDefersLoading(false);
1550 virtual void didReceiveData(blink::WebURLLoader
* loader
,
1553 int encoded_data_length
) {
1554 if (content::PepperPluginInstance::Get(instance_
)) {
1555 // Stream the data we received to the stream callback.
1556 stream_handler_
->DidStreamData(stream_handler_user_data_
,
1562 virtual void didFinishLoading(blink::WebURLLoader
* loader
,
1564 int64_t total_encoded_data_length
) {
1565 int32_t result
= success_
? PP_OK
: PP_ERROR_FAILED
;
1567 if (content::PepperPluginInstance::Get(instance_
))
1568 stream_handler_
->DidFinishStream(stream_handler_user_data_
, result
);
1572 virtual void didFail(blink::WebURLLoader
* loader
,
1573 const blink::WebURLError
& error
) {
1577 PP_Instance instance_
;
1578 scoped_ptr
<blink::WebURLLoader
> url_loader_
;
1579 std::string pexe_url_
;
1580 int32_t pexe_opt_level_
;
1581 const PPP_PexeStreamHandler
* stream_handler_
;
1582 void* stream_handler_user_data_
;
1584 int64_t expected_content_length_
;
1585 base::WeakPtrFactory
<PexeDownloader
> weak_factory_
;
1588 void StreamPexe(PP_Instance instance
,
1589 const char* pexe_url
,
1591 const PPP_PexeStreamHandler
* handler
,
1592 void* handler_user_data
) {
1593 content::PepperPluginInstance
* plugin_instance
=
1594 content::PepperPluginInstance::Get(instance
);
1595 if (!plugin_instance
) {
1596 base::MessageLoop::current()->PostTask(
1598 base::Bind(handler
->DidFinishStream
,
1600 static_cast<int32_t>(PP_ERROR_FAILED
)));
1604 GURL
gurl(pexe_url
);
1605 const blink::WebDocument
& document
=
1606 plugin_instance
->GetContainer()->element().document();
1607 scoped_ptr
<blink::WebURLLoader
> url_loader(
1608 CreateWebURLLoader(document
, gurl
));
1609 PexeDownloader
* downloader
= new PexeDownloader(instance
,
1616 blink::WebURLRequest url_request
= CreateWebURLRequest(document
, gurl
);
1617 // Mark the request as requesting a PNaCl bitcode file,
1618 // so that component updater can detect this user action.
1619 url_request
.addHTTPHeaderField(
1620 blink::WebString::fromUTF8("Accept"),
1621 blink::WebString::fromUTF8("application/x-pnacl, */*"));
1622 url_request
.setRequestContext(blink::WebURLRequest::RequestContextObject
);
1623 downloader
->Load(url_request
);
1626 const PPB_NaCl_Private nacl_interface
= {
1629 &BrokerDuplicateHandle
,
1630 &GetReadExecPnaclFd
,
1631 &CreateTemporaryFile
,
1632 &GetNumberOfProcessors
,
1633 &ReportTranslationFinished
,
1641 &RequestNaClManifest
,
1642 &GetManifestBaseURL
,
1643 &ProcessNaClManifest
,
1644 &ManifestGetProgramURL
,
1645 &GetPNaClResourceInfo
,
1646 &GetCpuFeatureAttrs
,
1648 &ReportSelLdrStatus
,
1656 const PPB_NaCl_Private
* GetNaClPrivateInterface() {
1657 return &nacl_interface
;