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 bool CanOpenViaFastPath(content::PepperPluginInstance
* plugin_instance
,
94 // Fast path only works for installed file URLs.
95 if (!gurl
.SchemeIs("chrome-extension"))
96 return PP_kInvalidFileHandle
;
98 // IMPORTANT: Make sure the document can request the given URL. If we don't
99 // check, a malicious app could probe the extension system. This enforces a
100 // same-origin policy which prevents the app from requesting resources from
102 blink::WebSecurityOrigin security_origin
=
103 plugin_instance
->GetContainer()->element().document().securityOrigin();
104 return security_origin
.canRequest(gurl
);
107 // This contains state that is produced by LaunchSelLdr() and consumed
108 // by StartPpapiProxy().
109 struct InstanceInfo
{
110 InstanceInfo() : plugin_pid(base::kNullProcessId
), plugin_child_id(0) {}
112 ppapi::PpapiPermissions permissions
;
113 base::ProcessId plugin_pid
;
115 IPC::ChannelHandle channel_handle
;
118 class NaClPluginInstance
{
120 NaClPluginInstance(PP_Instance instance
):
121 nexe_load_manager(instance
), pexe_size(0) {}
123 NexeLoadManager nexe_load_manager
;
124 scoped_ptr
<JsonManifest
> json_manifest
;
125 scoped_ptr
<InstanceInfo
> instance_info
;
127 // When translation is complete, this records the size of the pexe in
128 // bytes so that it can be reported in a later load event.
132 typedef base::ScopedPtrHashMap
<PP_Instance
, NaClPluginInstance
> InstanceMap
;
133 base::LazyInstance
<InstanceMap
> g_instance_map
= LAZY_INSTANCE_INITIALIZER
;
135 NaClPluginInstance
* GetNaClPluginInstance(PP_Instance instance
) {
136 InstanceMap
& map
= g_instance_map
.Get();
137 InstanceMap::iterator iter
= map
.find(instance
);
138 if (iter
== map
.end())
143 NexeLoadManager
* GetNexeLoadManager(PP_Instance instance
) {
144 NaClPluginInstance
* nacl_plugin_instance
= GetNaClPluginInstance(instance
);
145 if (!nacl_plugin_instance
)
147 return &nacl_plugin_instance
->nexe_load_manager
;
150 JsonManifest
* GetJsonManifest(PP_Instance instance
) {
151 NaClPluginInstance
* nacl_plugin_instance
= GetNaClPluginInstance(instance
);
152 if (!nacl_plugin_instance
)
154 return nacl_plugin_instance
->json_manifest
.get();
157 static const PP_NaClFileInfo kInvalidNaClFileInfo
= {
158 PP_kInvalidFileHandle
,
163 int GetRoutingID(PP_Instance instance
) {
164 // Check that we are on the main renderer thread.
165 DCHECK(content::RenderThread::Get());
166 content::RendererPpapiHost
* host
=
167 content::RendererPpapiHost::GetForPPInstance(instance
);
170 return host
->GetRoutingIDForWidget(instance
);
173 // Returns whether the channel_handle is valid or not.
174 bool IsValidChannelHandle(const IPC::ChannelHandle
& channel_handle
) {
175 if (channel_handle
.name
.empty()) {
179 #if defined(OS_POSIX)
180 if (channel_handle
.socket
.fd
== -1) {
188 void PostPPCompletionCallback(PP_CompletionCallback callback
,
190 ppapi::PpapiGlobals::Get()->GetMainThreadMessageLoop()->PostTask(
192 base::Bind(callback
.func
, callback
.user_data
, status
));
195 bool ManifestResolveKey(PP_Instance instance
,
196 bool is_helper_process
,
197 const std::string
& key
,
198 std::string
* full_url
,
199 PP_PNaClOptions
* pnacl_options
);
201 typedef base::Callback
<void(int32_t, const PP_NaClFileInfo
&)>
202 DownloadFileCallback
;
204 void DownloadFile(PP_Instance instance
,
205 const std::string
& url
,
206 const DownloadFileCallback
& callback
);
208 PP_Bool
StartPpapiProxy(PP_Instance instance
);
210 // Thin adapter from PPP_ManifestService to ManifestServiceChannel::Delegate.
211 // Note that user_data is managed by the caller of LaunchSelLdr. Please see
212 // also PP_ManifestService's comment for more details about resource
214 class ManifestServiceProxy
: public ManifestServiceChannel::Delegate
{
216 ManifestServiceProxy(PP_Instance pp_instance
, NaClAppProcessType process_type
)
217 : pp_instance_(pp_instance
), process_type_(process_type
) {}
219 ~ManifestServiceProxy() override
{}
221 void StartupInitializationComplete() override
{
222 if (StartPpapiProxy(pp_instance_
) == PP_TRUE
) {
223 NaClPluginInstance
* nacl_plugin_instance
=
224 GetNaClPluginInstance(pp_instance_
);
225 JsonManifest
* manifest
= GetJsonManifest(pp_instance_
);
226 if (nacl_plugin_instance
&& manifest
) {
227 NexeLoadManager
* load_manager
=
228 &nacl_plugin_instance
->nexe_load_manager
;
229 std::string full_url
;
230 PP_PNaClOptions pnacl_options
;
231 bool uses_nonsfi_mode
;
232 JsonManifest::ErrorInfo error_info
;
233 if (manifest
->GetProgramURL(&full_url
,
237 int64_t exe_size
= nacl_plugin_instance
->pexe_size
;
239 exe_size
= load_manager
->nexe_size();
240 load_manager
->ReportLoadSuccess(full_url
, exe_size
, exe_size
);
247 const std::string
& key
,
248 const ManifestServiceChannel::OpenResourceCallback
& callback
) override
{
249 DCHECK(ppapi::PpapiGlobals::Get()->GetMainThreadMessageLoop()->
250 BelongsToCurrentThread());
252 // For security hardening, disable open_resource() when it is isn't
253 // needed. PNaCl pexes can't use open_resource(), but general nexes
254 // and the PNaCl translator nexes may use it.
255 if (process_type_
!= kNativeNaClProcessType
&&
256 process_type_
!= kPNaClTranslatorProcessType
) {
258 base::MessageLoop::current()->PostTask(
260 base::Bind(callback
, base::Passed(base::File()), 0, 0));
265 // TODO(teravest): Clean up pnacl_options logic in JsonManifest so we don't
266 // have to initialize it like this here.
267 PP_PNaClOptions pnacl_options
;
268 pnacl_options
.translate
= PP_FALSE
;
269 pnacl_options
.is_debug
= PP_FALSE
;
270 pnacl_options
.use_subzero
= PP_FALSE
;
271 pnacl_options
.opt_level
= 2;
272 bool is_helper_process
= process_type_
== kPNaClTranslatorProcessType
;
273 if (!ManifestResolveKey(pp_instance_
, is_helper_process
, key
, &url
,
275 base::MessageLoop::current()->PostTask(
277 base::Bind(callback
, base::Passed(base::File()), 0, 0));
281 // We have to call DidDownloadFile, even if this object is destroyed, so
282 // that the handle inside PP_NaClFileInfo isn't leaked. This means that the
283 // callback passed to this function shouldn't have a weak pointer to an
286 // TODO(teravest): Make a type like PP_NaClFileInfo to use for DownloadFile
287 // that would close the file handle on destruction.
288 DownloadFile(pp_instance_
, url
,
289 base::Bind(&ManifestServiceProxy::DidDownloadFile
, callback
));
293 static void DidDownloadFile(
294 ManifestServiceChannel::OpenResourceCallback callback
,
296 const PP_NaClFileInfo
& file_info
) {
297 if (pp_error
!= PP_OK
) {
298 callback
.Run(base::File(), 0, 0);
301 callback
.Run(base::File(file_info
.handle
),
306 PP_Instance pp_instance_
;
307 NaClAppProcessType process_type_
;
308 DISALLOW_COPY_AND_ASSIGN(ManifestServiceProxy
);
311 blink::WebURLLoader
* CreateWebURLLoader(const blink::WebDocument
& document
,
313 blink::WebURLLoaderOptions options
;
314 options
.untrustedHTTP
= true;
316 // Options settings here follow the original behavior in the trusted
317 // plugin and PepperURLLoaderHost.
318 if (document
.securityOrigin().canRequest(gurl
)) {
319 options
.allowCredentials
= true;
322 options
.crossOriginRequestPolicy
=
323 blink::WebURLLoaderOptions::CrossOriginRequestPolicyUseAccessControl
;
325 return document
.frame()->createAssociatedURLLoader(options
);
328 blink::WebURLRequest
CreateWebURLRequest(const blink::WebDocument
& document
,
330 blink::WebURLRequest request
;
331 request
.initialize();
332 request
.setURL(gurl
);
333 request
.setFirstPartyForCookies(document
.firstPartyForCookies());
337 int32_t FileDownloaderToPepperError(FileDownloader::Status status
) {
339 case FileDownloader::SUCCESS
:
341 case FileDownloader::ACCESS_DENIED
:
342 return PP_ERROR_NOACCESS
;
343 case FileDownloader::FAILED
:
344 return PP_ERROR_FAILED
;
345 // No default case, to catch unhandled Status values.
347 return PP_ERROR_FAILED
;
350 NaClAppProcessType
PP_ToNaClAppProcessType(
351 PP_NaClAppProcessType pp_process_type
) {
352 #define STATICALLY_CHECK_NACLAPPPROCESSTYPE_EQ(pp, nonpp) \
353 static_assert(static_cast<int>(pp) == static_cast<int>(nonpp), \
354 "PP_NaClAppProcessType differs from NaClAppProcessType");
355 STATICALLY_CHECK_NACLAPPPROCESSTYPE_EQ(PP_UNKNOWN_NACL_PROCESS_TYPE
,
356 kUnknownNaClProcessType
);
357 STATICALLY_CHECK_NACLAPPPROCESSTYPE_EQ(PP_NATIVE_NACL_PROCESS_TYPE
,
358 kNativeNaClProcessType
);
359 STATICALLY_CHECK_NACLAPPPROCESSTYPE_EQ(PP_PNACL_PROCESS_TYPE
,
361 STATICALLY_CHECK_NACLAPPPROCESSTYPE_EQ(PP_PNACL_TRANSLATOR_PROCESS_TYPE
,
362 kPNaClTranslatorProcessType
);
363 STATICALLY_CHECK_NACLAPPPROCESSTYPE_EQ(PP_NUM_NACL_PROCESS_TYPES
,
364 kNumNaClProcessTypes
);
365 #undef STATICALLY_CHECK_NACLAPPPROCESSTYPE_EQ
366 DCHECK(pp_process_type
> PP_UNKNOWN_NACL_PROCESS_TYPE
&&
367 pp_process_type
< PP_NUM_NACL_PROCESS_TYPES
);
368 return static_cast<NaClAppProcessType
>(pp_process_type
);
371 // Launch NaCl's sel_ldr process.
372 void LaunchSelLdr(PP_Instance instance
,
373 PP_Bool main_service_runtime
,
374 const char* alleged_url
,
375 const PP_NaClFileInfo
* nexe_file_info
,
376 PP_Bool uses_nonsfi_mode
,
377 PP_NaClAppProcessType pp_process_type
,
379 PP_CompletionCallback callback
) {
380 CHECK(ppapi::PpapiGlobals::Get()->GetMainThreadMessageLoop()->
381 BelongsToCurrentThread());
382 NaClAppProcessType process_type
= PP_ToNaClAppProcessType(pp_process_type
);
383 // Create the manifest service proxy here, so on error case, it will be
384 // destructed (without passing it to ManifestServiceChannel).
385 scoped_ptr
<ManifestServiceChannel::Delegate
> manifest_service_proxy(
386 new ManifestServiceProxy(instance
, process_type
));
388 FileDescriptor result_socket
;
389 IPC::Sender
* sender
= content::RenderThread::Get();
391 int routing_id
= GetRoutingID(instance
);
392 NexeLoadManager
* load_manager
= GetNexeLoadManager(instance
);
393 DCHECK(load_manager
);
394 content::PepperPluginInstance
* plugin_instance
=
395 content::PepperPluginInstance::Get(instance
);
396 DCHECK(plugin_instance
);
397 if (!routing_id
|| !load_manager
|| !plugin_instance
) {
398 if (nexe_file_info
->handle
!= PP_kInvalidFileHandle
) {
399 base::File
closer(nexe_file_info
->handle
);
401 ppapi::PpapiGlobals::Get()->GetMainThreadMessageLoop()->PostTask(
402 FROM_HERE
, base::Bind(callback
.func
, callback
.user_data
,
403 static_cast<int32_t>(PP_ERROR_FAILED
)));
407 InstanceInfo instance_info
;
408 instance_info
.url
= GURL(alleged_url
);
410 uint32_t perm_bits
= ppapi::PERMISSION_NONE
;
411 // Conditionally block 'Dev' interfaces. We do this for the NaCl process, so
412 // it's clearer to developers when they are using 'Dev' inappropriately. We
413 // must also check on the trusted side of the proxy.
414 if (load_manager
->DevInterfacesEnabled())
415 perm_bits
|= ppapi::PERMISSION_DEV
;
416 instance_info
.permissions
=
417 ppapi::PpapiPermissions::GetForCommandLine(perm_bits
);
418 std::string error_message_string
;
419 NaClLaunchResult launch_result
;
421 IPC::PlatformFileForTransit nexe_for_transit
=
422 IPC::InvalidPlatformFileForTransit();
424 std::vector
<std::pair
<
425 std::string
/*key*/, std::string
/*url*/> > resource_files_to_prefetch
;
426 if (process_type
== kNativeNaClProcessType
&& uses_nonsfi_mode
) {
427 JsonManifest
* manifest
= GetJsonManifest(instance
);
429 manifest
->GetPrefetchableFiles(&resource_files_to_prefetch
);
430 for (size_t i
= 0; i
< resource_files_to_prefetch
.size(); ++i
) {
431 const GURL
gurl(resource_files_to_prefetch
[i
].second
);
432 // Important security check. Do not remove.
433 if (!CanOpenViaFastPath(plugin_instance
, gurl
)) {
434 resource_files_to_prefetch
.clear();
440 #if defined(OS_POSIX)
441 if (nexe_file_info
->handle
!= PP_kInvalidFileHandle
)
442 nexe_for_transit
= base::FileDescriptor(nexe_file_info
->handle
, true);
443 #elif defined(OS_WIN)
444 // Duplicate the handle on the browser side instead of the renderer.
445 // This is because BrokerGetFileForProcess isn't part of content/public, and
446 // it's simpler to do the duplication in the browser anyway.
447 nexe_for_transit
= nexe_file_info
->handle
;
449 #error Unsupported target platform.
451 if (!sender
->Send(new NaClHostMsg_LaunchNaCl(
453 instance_info
.url
.spec(),
455 nexe_file_info
->token_lo
,
456 nexe_file_info
->token_hi
,
457 resource_files_to_prefetch
,
460 PP_ToBool(uses_nonsfi_mode
),
463 &error_message_string
))) {
464 ppapi::PpapiGlobals::Get()->GetMainThreadMessageLoop()->PostTask(
466 base::Bind(callback
.func
, callback
.user_data
,
467 static_cast<int32_t>(PP_ERROR_FAILED
)));
471 load_manager
->set_nonsfi(PP_ToBool(uses_nonsfi_mode
));
473 if (!error_message_string
.empty()) {
474 if (PP_ToBool(main_service_runtime
)) {
475 load_manager
->ReportLoadError(PP_NACL_ERROR_SEL_LDR_LAUNCH
,
476 "ServiceRuntime: failed to start",
477 error_message_string
);
479 ppapi::PpapiGlobals::Get()->GetMainThreadMessageLoop()->PostTask(
481 base::Bind(callback
.func
, callback
.user_data
,
482 static_cast<int32_t>(PP_ERROR_FAILED
)));
485 result_socket
= launch_result
.imc_channel_handle
;
486 instance_info
.channel_handle
= launch_result
.ppapi_ipc_channel_handle
;
487 instance_info
.plugin_pid
= launch_result
.plugin_pid
;
488 instance_info
.plugin_child_id
= launch_result
.plugin_child_id
;
490 // Don't save instance_info if channel handle is invalid.
491 if (IsValidChannelHandle(instance_info
.channel_handle
)) {
492 NaClPluginInstance
* nacl_plugin_instance
= GetNaClPluginInstance(instance
);
493 nacl_plugin_instance
->instance_info
.reset(new InstanceInfo(instance_info
));
496 *(static_cast<NaClHandle
*>(imc_handle
)) = ToNativeHandle(result_socket
);
498 // Store the crash information shared memory handle.
499 load_manager
->set_crash_info_shmem_handle(
500 launch_result
.crash_info_shmem_handle
);
502 // Create the trusted plugin channel.
503 if (IsValidChannelHandle(launch_result
.trusted_ipc_channel_handle
)) {
504 bool report_exit_status
= PP_ToBool(main_service_runtime
);
505 scoped_ptr
<TrustedPluginChannel
> trusted_plugin_channel(
506 new TrustedPluginChannel(
508 launch_result
.trusted_ipc_channel_handle
,
509 content::RenderThread::Get()->GetShutdownEvent(),
510 report_exit_status
));
511 load_manager
->set_trusted_plugin_channel(trusted_plugin_channel
.Pass());
513 PostPPCompletionCallback(callback
, PP_ERROR_FAILED
);
517 // Create the manifest service handle as well.
518 if (IsValidChannelHandle(launch_result
.manifest_service_ipc_channel_handle
)) {
519 scoped_ptr
<ManifestServiceChannel
> manifest_service_channel(
520 new ManifestServiceChannel(
521 launch_result
.manifest_service_ipc_channel_handle
,
522 base::Bind(&PostPPCompletionCallback
, callback
),
523 manifest_service_proxy
.Pass(),
524 content::RenderThread::Get()->GetShutdownEvent()));
525 load_manager
->set_manifest_service_channel(
526 manifest_service_channel
.Pass());
530 PP_Bool
StartPpapiProxy(PP_Instance instance
) {
531 NexeLoadManager
* load_manager
= GetNexeLoadManager(instance
);
532 DCHECK(load_manager
);
536 content::PepperPluginInstance
* plugin_instance
=
537 content::PepperPluginInstance::Get(instance
);
538 if (!plugin_instance
) {
539 DLOG(ERROR
) << "GetInstance() failed";
543 NaClPluginInstance
* nacl_plugin_instance
= GetNaClPluginInstance(instance
);
544 if (!nacl_plugin_instance
->instance_info
) {
545 DLOG(ERROR
) << "Could not find instance ID";
548 scoped_ptr
<InstanceInfo
> instance_info
=
549 nacl_plugin_instance
->instance_info
.Pass();
551 PP_ExternalPluginResult result
= plugin_instance
->SwitchToOutOfProcessProxy(
552 base::FilePath().AppendASCII(instance_info
->url
.spec()),
553 instance_info
->permissions
,
554 instance_info
->channel_handle
,
555 instance_info
->plugin_pid
,
556 instance_info
->plugin_child_id
);
558 if (result
== PP_EXTERNAL_PLUGIN_OK
) {
559 // Log the amound of time that has passed between the trusted plugin being
560 // initialized and the untrusted plugin being initialized. This is
561 // (roughly) the cost of using NaCl, in terms of startup time.
562 load_manager
->ReportStartupOverhead();
564 } else if (result
== PP_EXTERNAL_PLUGIN_ERROR_MODULE
) {
565 load_manager
->ReportLoadError(PP_NACL_ERROR_START_PROXY_MODULE
,
566 "could not initialize module.");
567 } else if (result
== PP_EXTERNAL_PLUGIN_ERROR_INSTANCE
) {
568 load_manager
->ReportLoadError(PP_NACL_ERROR_START_PROXY_MODULE
,
569 "could not create instance.");
574 int UrandomFD(void) {
575 #if defined(OS_POSIX)
576 return base::GetUrandomFD();
582 int32_t BrokerDuplicateHandle(PP_FileHandle source_handle
,
584 PP_FileHandle
* target_handle
,
585 uint32_t desired_access
,
588 return content::BrokerDuplicateHandle(source_handle
, process_id
,
589 target_handle
, desired_access
,
596 // Convert a URL to a filename for GetReadonlyPnaclFd.
597 // Must be kept in sync with PnaclCanOpenFile() in
598 // components/nacl/browser/nacl_file_host.cc.
599 std::string
PnaclComponentURLToFilename(const std::string
& url
) {
600 // PNaCl component URLs aren't arbitrary URLs; they are always either
601 // generated from ManifestResolveKey or PnaclResources::ReadResourceInfo.
602 // So, it's safe to just use string parsing operations here instead of
604 DCHECK(StartsWithASCII(url
, kPNaClTranslatorBaseUrl
, true));
605 std::string r
= url
.substr(std::string(kPNaClTranslatorBaseUrl
).length());
607 // Use white-listed-chars.
609 static const char* white_list
= "abcdefghijklmnopqrstuvwxyz0123456789_";
610 replace_pos
= r
.find_first_not_of(white_list
);
611 while(replace_pos
!= std::string::npos
) {
612 r
= r
.replace(replace_pos
, 1, "_");
613 replace_pos
= r
.find_first_not_of(white_list
);
618 PP_FileHandle
GetReadonlyPnaclFd(const char* url
,
621 uint64_t* nonce_hi
) {
622 std::string filename
= PnaclComponentURLToFilename(url
);
623 IPC::PlatformFileForTransit out_fd
= IPC::InvalidPlatformFileForTransit();
624 IPC::Sender
* sender
= content::RenderThread::Get();
626 if (!sender
->Send(new NaClHostMsg_GetReadonlyPnaclFD(
627 std::string(filename
), is_executable
,
628 &out_fd
, nonce_lo
, nonce_hi
))) {
629 return PP_kInvalidFileHandle
;
631 if (out_fd
== IPC::InvalidPlatformFileForTransit()) {
632 return PP_kInvalidFileHandle
;
634 return IPC::PlatformFileForTransitToPlatformFile(out_fd
);
637 void GetReadExecPnaclFd(const char* url
,
638 PP_NaClFileInfo
* out_file_info
) {
639 *out_file_info
= kInvalidNaClFileInfo
;
640 out_file_info
->handle
= GetReadonlyPnaclFd(url
, true /* is_executable */,
641 &out_file_info
->token_lo
,
642 &out_file_info
->token_hi
);
645 PP_FileHandle
CreateTemporaryFile(PP_Instance instance
) {
646 IPC::PlatformFileForTransit transit_fd
= IPC::InvalidPlatformFileForTransit();
647 IPC::Sender
* sender
= content::RenderThread::Get();
649 if (!sender
->Send(new NaClHostMsg_NaClCreateTemporaryFile(
651 return PP_kInvalidFileHandle
;
654 if (transit_fd
== IPC::InvalidPlatformFileForTransit()) {
655 return PP_kInvalidFileHandle
;
658 return IPC::PlatformFileForTransitToPlatformFile(transit_fd
);
661 int32_t GetNumberOfProcessors() {
662 IPC::Sender
* sender
= content::RenderThread::Get();
664 int32_t num_processors
= 1;
665 return sender
->Send(new NaClHostMsg_NaClGetNumProcessors(&num_processors
)) ?
669 void GetNexeFd(PP_Instance instance
,
670 const std::string
& pexe_url
,
672 const base::Time
& last_modified_time
,
673 const std::string
& etag
,
674 bool has_no_store_header
,
676 base::Callback
<void(int32_t, bool, PP_FileHandle
)> callback
) {
677 if (!InitializePnaclResourceHost()) {
678 ppapi::PpapiGlobals::Get()->GetMainThreadMessageLoop()->PostTask(
681 static_cast<int32_t>(PP_ERROR_FAILED
),
683 PP_kInvalidFileHandle
));
687 PnaclCacheInfo cache_info
;
688 cache_info
.pexe_url
= GURL(pexe_url
);
689 // TODO(dschuff): Get this value from the pnacl json file after it
690 // rolls in from NaCl.
691 cache_info
.abi_version
= 1;
692 cache_info
.opt_level
= opt_level
;
693 cache_info
.last_modified
= last_modified_time
;
694 cache_info
.etag
= etag
;
695 cache_info
.has_no_store_header
= has_no_store_header
;
696 cache_info
.use_subzero
= use_subzero
;
697 cache_info
.sandbox_isa
= GetSandboxArch();
698 cache_info
.extra_flags
= GetCpuFeatures();
700 g_pnacl_resource_host
.Get()->RequestNexeFd(
701 GetRoutingID(instance
),
707 void LogTranslationFinishedUMA(const std::string
& uma_suffix
,
709 int32_t unknown_opt_level
,
712 int64_t compile_time_us
,
713 base::TimeDelta total_time
) {
714 HistogramEnumerate("NaCl.Options.PNaCl.OptLevel" + uma_suffix
, opt_level
,
715 unknown_opt_level
+ 1);
716 HistogramKBPerSec("NaCl.Perf.PNaClLoadTime.CompileKBPerSec" + uma_suffix
,
717 pexe_size
/ 1024, compile_time_us
);
718 HistogramSizeKB("NaCl.Perf.Size.PNaClTranslatedNexe" + uma_suffix
,
720 HistogramSizeKB("NaCl.Perf.Size.Pexe" + uma_suffix
, pexe_size
/ 1024);
721 HistogramRatio("NaCl.Perf.Size.PexeNexeSizePct" + uma_suffix
, pexe_size
,
723 HistogramTimeTranslation(
724 "NaCl.Perf.PNaClLoadTime.TotalUncachedTime" + uma_suffix
,
725 total_time
.InMilliseconds());
727 "NaCl.Perf.PNaClLoadTime.TotalUncachedKBPerSec" + uma_suffix
,
728 pexe_size
/ 1024, total_time
.InMicroseconds());
731 void ReportTranslationFinished(PP_Instance instance
,
737 int64_t compile_time_us
) {
738 NexeLoadManager
* load_manager
= GetNexeLoadManager(instance
);
739 DCHECK(load_manager
);
740 if (success
== PP_TRUE
&& load_manager
) {
741 base::TimeDelta total_time
=
742 base::Time::Now() - load_manager
->pnacl_start_time();
743 static const int32_t kUnknownOptLevel
= 4;
744 if (opt_level
< 0 || opt_level
> 3)
745 opt_level
= kUnknownOptLevel
;
746 // Log twice: once to cover all PNaCl UMA, and then a second
747 // time with the more specific UMA (Subzero vs LLC).
748 std::string
uma_suffix(use_subzero
? ".Subzero" : ".LLC");
749 LogTranslationFinishedUMA("", opt_level
, kUnknownOptLevel
, nexe_size
,
750 pexe_size
, compile_time_us
, total_time
);
751 LogTranslationFinishedUMA(uma_suffix
, opt_level
, kUnknownOptLevel
,
752 nexe_size
, pexe_size
, compile_time_us
,
756 // If the resource host isn't initialized, don't try to do that here.
757 // Just return because something is already very wrong.
758 if (g_pnacl_resource_host
.Get().get() == NULL
)
760 g_pnacl_resource_host
.Get()->ReportTranslationFinished(instance
, success
);
762 // Record the pexe size for reporting in a later load event.
763 NaClPluginInstance
* nacl_plugin_instance
= GetNaClPluginInstance(instance
);
764 if (nacl_plugin_instance
) {
765 nacl_plugin_instance
->pexe_size
= pexe_size
;
769 PP_FileHandle
OpenNaClExecutable(PP_Instance instance
,
770 const char* file_url
,
772 uint64_t* nonce_hi
) {
773 NexeLoadManager
* load_manager
= GetNexeLoadManager(instance
);
774 DCHECK(load_manager
);
776 return PP_kInvalidFileHandle
;
778 content::PepperPluginInstance
* plugin_instance
=
779 content::PepperPluginInstance::Get(instance
);
780 if (!plugin_instance
)
781 return PP_kInvalidFileHandle
;
784 // Important security check. Do not remove.
785 if (!CanOpenViaFastPath(plugin_instance
, gurl
))
786 return PP_kInvalidFileHandle
;
788 IPC::PlatformFileForTransit out_fd
= IPC::InvalidPlatformFileForTransit();
789 IPC::Sender
* sender
= content::RenderThread::Get();
793 base::FilePath file_path
;
795 new NaClHostMsg_OpenNaClExecutable(GetRoutingID(instance
),
797 !load_manager
->nonsfi(),
801 return PP_kInvalidFileHandle
;
804 if (out_fd
== IPC::InvalidPlatformFileForTransit())
805 return PP_kInvalidFileHandle
;
807 return IPC::PlatformFileForTransitToPlatformFile(out_fd
);
810 void DispatchEvent(PP_Instance instance
,
811 PP_NaClEventType event_type
,
812 const char* resource_url
,
813 PP_Bool length_is_computable
,
814 uint64_t loaded_bytes
,
815 uint64_t total_bytes
) {
816 ProgressEvent
event(event_type
,
818 PP_ToBool(length_is_computable
),
821 DispatchProgressEvent(instance
, event
);
824 void ReportLoadError(PP_Instance instance
,
826 const char* error_message
) {
827 NexeLoadManager
* load_manager
= GetNexeLoadManager(instance
);
829 load_manager
->ReportLoadError(error
, error_message
);
832 void InstanceCreated(PP_Instance instance
) {
833 InstanceMap
& map
= g_instance_map
.Get();
834 CHECK(map
.find(instance
) == map
.end()); // Sanity check.
835 scoped_ptr
<NaClPluginInstance
> new_instance(new NaClPluginInstance(instance
));
836 map
.add(instance
, new_instance
.Pass());
839 void InstanceDestroyed(PP_Instance instance
) {
840 InstanceMap
& map
= g_instance_map
.Get();
841 InstanceMap::iterator iter
= map
.find(instance
);
842 CHECK(iter
!= map
.end());
843 // The erase may call NexeLoadManager's destructor prior to removing it from
844 // the map. In that case, it is possible for the trusted Plugin to re-enter
845 // the NexeLoadManager (e.g., by calling ReportLoadError). Passing out the
846 // NexeLoadManager to a local scoped_ptr just ensures that its entry is gone
847 // from the map prior to the destructor being invoked.
848 scoped_ptr
<NaClPluginInstance
> temp(map
.take(instance
));
852 PP_Bool
NaClDebugEnabledForURL(const char* alleged_nmf_url
) {
853 if (!base::CommandLine::ForCurrentProcess()->HasSwitch(
854 switches::kEnableNaClDebug
))
856 IPC::Sender
* sender
= content::RenderThread::Get();
858 bool should_debug
= false;
860 sender
->Send(new NaClHostMsg_NaClDebugEnabledForURL(GURL(alleged_nmf_url
),
865 void Vlog(const char* message
) {
869 void InitializePlugin(PP_Instance instance
,
872 const char* argv
[]) {
873 NexeLoadManager
* load_manager
= GetNexeLoadManager(instance
);
874 DCHECK(load_manager
);
876 load_manager
->InitializePlugin(argc
, argn
, argv
);
879 void DownloadManifestToBuffer(PP_Instance instance
,
880 struct PP_CompletionCallback callback
);
882 bool CreateJsonManifest(PP_Instance instance
,
883 const std::string
& manifest_url
,
884 const std::string
& manifest_data
);
886 void RequestNaClManifest(PP_Instance instance
,
887 PP_CompletionCallback callback
) {
888 NexeLoadManager
* load_manager
= GetNexeLoadManager(instance
);
889 DCHECK(load_manager
);
891 ppapi::PpapiGlobals::Get()->GetMainThreadMessageLoop()->PostTask(
893 base::Bind(callback
.func
, callback
.user_data
,
894 static_cast<int32_t>(PP_ERROR_FAILED
)));
898 std::string url
= load_manager
->GetManifestURLArgument();
899 if (url
.empty() || !load_manager
->RequestNaClManifest(url
)) {
900 ppapi::PpapiGlobals::Get()->GetMainThreadMessageLoop()->PostTask(
902 base::Bind(callback
.func
, callback
.user_data
,
903 static_cast<int32_t>(PP_ERROR_FAILED
)));
907 const GURL
& base_url
= load_manager
->manifest_base_url();
908 if (base_url
.SchemeIs("data")) {
910 std::string mime_type
;
913 int32_t error
= PP_ERROR_FAILED
;
914 if (net::DataURL::Parse(gurl
, &mime_type
, &charset
, &data
)) {
915 if (data
.size() <= ManifestDownloader::kNaClManifestMaxFileBytes
) {
916 if (CreateJsonManifest(instance
, base_url
.spec(), data
))
919 load_manager
->ReportLoadError(PP_NACL_ERROR_MANIFEST_TOO_LARGE
,
920 "manifest file too large.");
923 load_manager
->ReportLoadError(PP_NACL_ERROR_MANIFEST_LOAD_URL
,
924 "could not load manifest url.");
926 ppapi::PpapiGlobals::Get()->GetMainThreadMessageLoop()->PostTask(
928 base::Bind(callback
.func
, callback
.user_data
, error
));
930 DownloadManifestToBuffer(instance
, callback
);
934 PP_Var
GetManifestBaseURL(PP_Instance instance
) {
935 NexeLoadManager
* load_manager
= GetNexeLoadManager(instance
);
936 DCHECK(load_manager
);
938 return PP_MakeUndefined();
939 const GURL
& gurl
= load_manager
->manifest_base_url();
940 if (!gurl
.is_valid())
941 return PP_MakeUndefined();
942 return ppapi::StringVar::StringToPPVar(gurl
.spec());
945 void ProcessNaClManifest(PP_Instance instance
, const char* program_url
) {
946 nacl::NexeLoadManager
* load_manager
= GetNexeLoadManager(instance
);
948 load_manager
->ProcessNaClManifest(program_url
);
951 void DownloadManifestToBufferCompletion(PP_Instance instance
,
952 struct PP_CompletionCallback callback
,
953 base::Time start_time
,
954 PP_NaClError pp_nacl_error
,
955 const std::string
& data
);
957 void DownloadManifestToBuffer(PP_Instance instance
,
958 struct PP_CompletionCallback callback
) {
959 nacl::NexeLoadManager
* load_manager
= GetNexeLoadManager(instance
);
960 DCHECK(load_manager
);
961 content::PepperPluginInstance
* plugin_instance
=
962 content::PepperPluginInstance::Get(instance
);
963 if (!load_manager
|| !plugin_instance
) {
964 ppapi::PpapiGlobals::Get()->GetMainThreadMessageLoop()->PostTask(
966 base::Bind(callback
.func
, callback
.user_data
,
967 static_cast<int32_t>(PP_ERROR_FAILED
)));
969 const blink::WebDocument
& document
=
970 plugin_instance
->GetContainer()->element().document();
972 const GURL
& gurl
= load_manager
->manifest_base_url();
973 scoped_ptr
<blink::WebURLLoader
> url_loader(
974 CreateWebURLLoader(document
, gurl
));
975 blink::WebURLRequest request
= CreateWebURLRequest(document
, gurl
);
977 // ManifestDownloader deletes itself after invoking the callback.
978 ManifestDownloader
* manifest_downloader
= new ManifestDownloader(
980 load_manager
->is_installed(),
981 base::Bind(DownloadManifestToBufferCompletion
,
982 instance
, callback
, base::Time::Now()));
983 manifest_downloader
->Load(request
);
986 void DownloadManifestToBufferCompletion(PP_Instance instance
,
987 struct PP_CompletionCallback callback
,
988 base::Time start_time
,
989 PP_NaClError pp_nacl_error
,
990 const std::string
& data
) {
991 base::TimeDelta download_time
= base::Time::Now() - start_time
;
992 HistogramTimeSmall("NaCl.Perf.StartupTime.ManifestDownload",
993 download_time
.InMilliseconds());
995 nacl::NexeLoadManager
* load_manager
= GetNexeLoadManager(instance
);
997 callback
.func(callback
.user_data
, PP_ERROR_ABORTED
);
1002 switch (pp_nacl_error
) {
1003 case PP_NACL_ERROR_LOAD_SUCCESS
:
1006 case PP_NACL_ERROR_MANIFEST_LOAD_URL
:
1007 pp_error
= PP_ERROR_FAILED
;
1008 load_manager
->ReportLoadError(PP_NACL_ERROR_MANIFEST_LOAD_URL
,
1009 "could not load manifest url.");
1011 case PP_NACL_ERROR_MANIFEST_TOO_LARGE
:
1012 pp_error
= PP_ERROR_FILETOOBIG
;
1013 load_manager
->ReportLoadError(PP_NACL_ERROR_MANIFEST_TOO_LARGE
,
1014 "manifest file too large.");
1016 case PP_NACL_ERROR_MANIFEST_NOACCESS_URL
:
1017 pp_error
= PP_ERROR_NOACCESS
;
1018 load_manager
->ReportLoadError(PP_NACL_ERROR_MANIFEST_NOACCESS_URL
,
1019 "access to manifest url was denied.");
1023 pp_error
= PP_ERROR_FAILED
;
1024 load_manager
->ReportLoadError(PP_NACL_ERROR_MANIFEST_LOAD_URL
,
1025 "could not load manifest url.");
1028 if (pp_error
== PP_OK
) {
1029 std::string base_url
= load_manager
->manifest_base_url().spec();
1030 if (!CreateJsonManifest(instance
, base_url
, data
))
1031 pp_error
= PP_ERROR_FAILED
;
1033 callback
.func(callback
.user_data
, pp_error
);
1036 bool CreateJsonManifest(PP_Instance instance
,
1037 const std::string
& manifest_url
,
1038 const std::string
& manifest_data
) {
1039 HistogramSizeKB("NaCl.Perf.Size.Manifest",
1040 static_cast<int32_t>(manifest_data
.length() / 1024));
1042 nacl::NexeLoadManager
* load_manager
= GetNexeLoadManager(instance
);
1046 const char* isa_type
;
1047 if (load_manager
->IsPNaCl())
1048 isa_type
= kPortableArch
;
1050 isa_type
= GetSandboxArch();
1052 scoped_ptr
<nacl::JsonManifest
> j(
1053 new nacl::JsonManifest(
1054 manifest_url
.c_str(),
1056 IsNonSFIModeEnabled(),
1057 PP_ToBool(NaClDebugEnabledForURL(manifest_url
.c_str()))));
1058 JsonManifest::ErrorInfo error_info
;
1059 if (j
->Init(manifest_data
.c_str(), &error_info
)) {
1060 GetNaClPluginInstance(instance
)->json_manifest
.reset(j
.release());
1063 load_manager
->ReportLoadError(error_info
.error
, error_info
.string
);
1067 PP_Bool
ManifestGetProgramURL(PP_Instance instance
,
1068 PP_Var
* pp_full_url
,
1069 PP_PNaClOptions
* pnacl_options
,
1070 PP_Bool
* pp_uses_nonsfi_mode
) {
1071 nacl::NexeLoadManager
* load_manager
= GetNexeLoadManager(instance
);
1073 JsonManifest
* manifest
= GetJsonManifest(instance
);
1074 if (manifest
== NULL
)
1077 bool uses_nonsfi_mode
;
1078 std::string full_url
;
1079 JsonManifest::ErrorInfo error_info
;
1080 if (manifest
->GetProgramURL(&full_url
, pnacl_options
, &uses_nonsfi_mode
,
1082 *pp_full_url
= ppapi::StringVar::StringToPPVar(full_url
);
1083 *pp_uses_nonsfi_mode
= PP_FromBool(uses_nonsfi_mode
);
1084 // Check if we should use Subzero (x86-32 / non-debugging case for now).
1085 if (pnacl_options
->opt_level
== 0 && !pnacl_options
->is_debug
&&
1086 strcmp(GetSandboxArch(), "x86-32") == 0 &&
1087 base::CommandLine::ForCurrentProcess()->HasSwitch(
1088 switches::kEnablePNaClSubzero
)) {
1089 pnacl_options
->use_subzero
= PP_TRUE
;
1090 // Subzero -O2 is closer to LLC -O0, so indicate -O2.
1091 pnacl_options
->opt_level
= 2;
1097 load_manager
->ReportLoadError(error_info
.error
, error_info
.string
);
1101 bool ManifestResolveKey(PP_Instance instance
,
1102 bool is_helper_process
,
1103 const std::string
& key
,
1104 std::string
* full_url
,
1105 PP_PNaClOptions
* pnacl_options
) {
1106 // For "helper" processes (llc and ld, for PNaCl translation), we resolve
1107 // keys manually as there is no existing .nmf file to parse.
1108 if (is_helper_process
) {
1109 pnacl_options
->translate
= PP_FALSE
;
1110 // We can only resolve keys in the files/ namespace.
1111 const std::string kFilesPrefix
= "files/";
1112 if (key
.find(kFilesPrefix
) == std::string::npos
) {
1113 nacl::NexeLoadManager
* load_manager
= GetNexeLoadManager(instance
);
1115 load_manager
->ReportLoadError(PP_NACL_ERROR_MANIFEST_RESOLVE_URL
,
1116 "key did not start with files/");
1119 std::string key_basename
= key
.substr(kFilesPrefix
.length());
1120 *full_url
= std::string(kPNaClTranslatorBaseUrl
) + GetSandboxArch() + "/" +
1125 JsonManifest
* manifest
= GetJsonManifest(instance
);
1126 if (manifest
== NULL
)
1129 return manifest
->ResolveKey(key
, full_url
, pnacl_options
);
1132 PP_Bool
GetPNaClResourceInfo(PP_Instance instance
,
1133 PP_Var
* llc_tool_name
,
1134 PP_Var
* ld_tool_name
,
1135 PP_Var
* subzero_tool_name
) {
1136 static const char kFilename
[] = "chrome://pnacl-translator/pnacl.json";
1137 NexeLoadManager
* load_manager
= GetNexeLoadManager(instance
);
1138 DCHECK(load_manager
);
1142 uint64_t nonce_lo
= 0;
1143 uint64_t nonce_hi
= 0;
1144 base::File
file(GetReadonlyPnaclFd(kFilename
, false /* is_executable */,
1145 &nonce_lo
, &nonce_hi
));
1146 if (!file
.IsValid()) {
1147 load_manager
->ReportLoadError(
1148 PP_NACL_ERROR_PNACL_RESOURCE_FETCH
,
1149 "The Portable Native Client (pnacl) component is not "
1150 "installed. Please consult chrome://components for more "
1155 base::File::Info file_info
;
1156 if (!file
.GetInfo(&file_info
)) {
1157 load_manager
->ReportLoadError(
1158 PP_NACL_ERROR_PNACL_RESOURCE_FETCH
,
1159 std::string("GetPNaClResourceInfo, GetFileInfo failed for: ") +
1164 if (file_info
.size
> 1 << 20) {
1165 load_manager
->ReportLoadError(
1166 PP_NACL_ERROR_PNACL_RESOURCE_FETCH
,
1167 std::string("GetPNaClResourceInfo, file too large: ") + kFilename
);
1171 scoped_ptr
<char[]> buffer(new char[file_info
.size
+ 1]);
1172 if (buffer
.get() == NULL
) {
1173 load_manager
->ReportLoadError(
1174 PP_NACL_ERROR_PNACL_RESOURCE_FETCH
,
1175 std::string("GetPNaClResourceInfo, couldn't allocate for: ") +
1180 int rc
= file
.Read(0, buffer
.get(), file_info
.size
);
1182 load_manager
->ReportLoadError(
1183 PP_NACL_ERROR_PNACL_RESOURCE_FETCH
,
1184 std::string("GetPNaClResourceInfo, reading failed for: ") + kFilename
);
1188 // Null-terminate the bytes we we read from the file.
1189 buffer
.get()[rc
] = 0;
1191 // Expect the JSON file to contain a top-level object (dictionary).
1192 Json::Reader json_reader
;
1193 Json::Value json_data
;
1194 if (!json_reader
.parse(buffer
.get(), json_data
)) {
1195 load_manager
->ReportLoadError(
1196 PP_NACL_ERROR_PNACL_RESOURCE_FETCH
,
1197 std::string("Parsing resource info failed: JSON parse error: ") +
1198 json_reader
.getFormattedErrorMessages());
1202 if (!json_data
.isObject()) {
1203 load_manager
->ReportLoadError(
1204 PP_NACL_ERROR_PNACL_RESOURCE_FETCH
,
1205 "Parsing resource info failed: Malformed JSON dictionary");
1209 if (json_data
.isMember("pnacl-llc-name")) {
1210 Json::Value json_name
= json_data
["pnacl-llc-name"];
1211 if (json_name
.isString()) {
1212 *llc_tool_name
= ppapi::StringVar::StringToPPVar(json_name
.asString());
1216 if (json_data
.isMember("pnacl-ld-name")) {
1217 Json::Value json_name
= json_data
["pnacl-ld-name"];
1218 if (json_name
.isString()) {
1219 *ld_tool_name
= ppapi::StringVar::StringToPPVar(json_name
.asString());
1223 if (json_data
.isMember("pnacl-sz-name")) {
1224 Json::Value json_name
= json_data
["pnacl-sz-name"];
1225 if (json_name
.isString()) {
1226 *subzero_tool_name
=
1227 ppapi::StringVar::StringToPPVar(json_name
.asString());
1230 // TODO(jvoung): remove fallback after one chrome release
1231 // or when we bump the kMinPnaclVersion.
1232 // TODO(jvoung): Just use strings instead of PP_Var!
1233 *subzero_tool_name
= ppapi::StringVar::StringToPPVar("pnacl-sz.nexe");
1239 PP_Var
GetCpuFeatureAttrs() {
1240 return ppapi::StringVar::StringToPPVar(GetCpuFeatures());
1243 // Encapsulates some of the state for a call to DownloadNexe to prevent
1244 // argument lists from getting too long.
1245 struct DownloadNexeRequest
{
1246 PP_Instance instance
;
1248 PP_CompletionCallback callback
;
1249 base::Time start_time
;
1252 // A utility class to ensure that we don't send progress events more often than
1253 // every 10ms for a given file.
1254 class ProgressEventRateLimiter
{
1256 explicit ProgressEventRateLimiter(PP_Instance instance
)
1257 : instance_(instance
) { }
1259 void ReportProgress(const std::string
& url
,
1260 int64_t total_bytes_received
,
1261 int64_t total_bytes_to_be_received
) {
1262 base::Time now
= base::Time::Now();
1263 if (now
- last_event_
> base::TimeDelta::FromMilliseconds(10)) {
1264 DispatchProgressEvent(instance_
,
1265 ProgressEvent(PP_NACL_EVENT_PROGRESS
,
1267 total_bytes_to_be_received
>= 0,
1268 total_bytes_received
,
1269 total_bytes_to_be_received
));
1275 PP_Instance instance_
;
1276 base::Time last_event_
;
1279 void DownloadNexeCompletion(const DownloadNexeRequest
& request
,
1280 PP_NaClFileInfo
* out_file_info
,
1281 FileDownloader::Status status
,
1282 base::File target_file
,
1285 void DownloadNexe(PP_Instance instance
,
1287 PP_NaClFileInfo
* out_file_info
,
1288 PP_CompletionCallback callback
) {
1290 CHECK(out_file_info
);
1291 DownloadNexeRequest request
;
1292 request
.instance
= instance
;
1294 request
.callback
= callback
;
1295 request
.start_time
= base::Time::Now();
1297 // Try the fast path for retrieving the file first.
1298 PP_FileHandle handle
= OpenNaClExecutable(instance
,
1300 &out_file_info
->token_lo
,
1301 &out_file_info
->token_hi
);
1302 if (handle
!= PP_kInvalidFileHandle
) {
1303 DownloadNexeCompletion(request
,
1305 FileDownloader::SUCCESS
,
1311 // The fast path didn't work, we'll fetch the file using URLLoader and write
1312 // it to local storage.
1313 base::File
target_file(CreateTemporaryFile(instance
));
1316 content::PepperPluginInstance
* plugin_instance
=
1317 content::PepperPluginInstance::Get(instance
);
1318 if (!plugin_instance
) {
1319 ppapi::PpapiGlobals::Get()->GetMainThreadMessageLoop()->PostTask(
1321 base::Bind(callback
.func
, callback
.user_data
,
1322 static_cast<int32_t>(PP_ERROR_FAILED
)));
1324 const blink::WebDocument
& document
=
1325 plugin_instance
->GetContainer()->element().document();
1326 scoped_ptr
<blink::WebURLLoader
> url_loader(
1327 CreateWebURLLoader(document
, gurl
));
1328 blink::WebURLRequest url_request
= CreateWebURLRequest(document
, gurl
);
1330 ProgressEventRateLimiter
* tracker
= new ProgressEventRateLimiter(instance
);
1332 // FileDownloader deletes itself after invoking DownloadNexeCompletion.
1333 FileDownloader
* file_downloader
= new FileDownloader(
1336 base::Bind(&DownloadNexeCompletion
, request
, out_file_info
),
1337 base::Bind(&ProgressEventRateLimiter::ReportProgress
,
1338 base::Owned(tracker
), std::string(url
)));
1339 file_downloader
->Load(url_request
);
1342 void DownloadNexeCompletion(const DownloadNexeRequest
& request
,
1343 PP_NaClFileInfo
* out_file_info
,
1344 FileDownloader::Status status
,
1345 base::File target_file
,
1347 int32_t pp_error
= FileDownloaderToPepperError(status
);
1348 int64_t bytes_read
= -1;
1349 if (pp_error
== PP_OK
&& target_file
.IsValid()) {
1350 base::File::Info info
;
1351 if (target_file
.GetInfo(&info
))
1352 bytes_read
= info
.size
;
1355 if (bytes_read
== -1) {
1356 target_file
.Close();
1357 pp_error
= PP_ERROR_FAILED
;
1360 base::TimeDelta download_time
= base::Time::Now() - request
.start_time
;
1362 NexeLoadManager
* load_manager
= GetNexeLoadManager(request
.instance
);
1364 load_manager
->NexeFileDidOpen(pp_error
,
1372 if (pp_error
== PP_OK
&& target_file
.IsValid())
1373 out_file_info
->handle
= target_file
.TakePlatformFile();
1375 out_file_info
->handle
= PP_kInvalidFileHandle
;
1377 request
.callback
.func(request
.callback
.user_data
, pp_error
);
1380 void DownloadFileCompletion(
1381 const DownloadFileCallback
& callback
,
1382 FileDownloader::Status status
,
1385 int32_t pp_error
= FileDownloaderToPepperError(status
);
1386 PP_NaClFileInfo file_info
;
1387 if (pp_error
== PP_OK
) {
1388 file_info
.handle
= file
.TakePlatformFile();
1389 file_info
.token_lo
= 0;
1390 file_info
.token_hi
= 0;
1392 file_info
= kInvalidNaClFileInfo
;
1395 callback
.Run(pp_error
, file_info
);
1398 void DownloadFile(PP_Instance instance
,
1399 const std::string
& url
,
1400 const DownloadFileCallback
& callback
) {
1401 DCHECK(ppapi::PpapiGlobals::Get()->GetMainThreadMessageLoop()->
1402 BelongsToCurrentThread());
1404 NexeLoadManager
* load_manager
= GetNexeLoadManager(instance
);
1405 DCHECK(load_manager
);
1406 if (!load_manager
) {
1407 base::MessageLoop::current()->PostTask(
1409 base::Bind(callback
,
1410 static_cast<int32_t>(PP_ERROR_FAILED
),
1411 kInvalidNaClFileInfo
));
1415 // Handle special PNaCl support files which are installed on the user's
1417 if (url
.find(kPNaClTranslatorBaseUrl
, 0) == 0) {
1418 PP_NaClFileInfo file_info
= kInvalidNaClFileInfo
;
1419 PP_FileHandle handle
= GetReadonlyPnaclFd(url
.c_str(),
1420 false /* is_executable */,
1421 &file_info
.token_lo
,
1422 &file_info
.token_hi
);
1423 if (handle
== PP_kInvalidFileHandle
) {
1424 base::MessageLoop::current()->PostTask(
1426 base::Bind(callback
,
1427 static_cast<int32_t>(PP_ERROR_FAILED
),
1428 kInvalidNaClFileInfo
));
1431 file_info
.handle
= handle
;
1432 base::MessageLoop::current()->PostTask(
1434 base::Bind(callback
, static_cast<int32_t>(PP_OK
), file_info
));
1438 // We have to ensure that this url resolves relative to the plugin base url
1439 // before downloading it.
1440 const GURL
& test_gurl
= load_manager
->plugin_base_url().Resolve(url
);
1441 if (!test_gurl
.is_valid()) {
1442 base::MessageLoop::current()->PostTask(
1444 base::Bind(callback
,
1445 static_cast<int32_t>(PP_ERROR_FAILED
),
1446 kInvalidNaClFileInfo
));
1450 // Try the fast path for retrieving the file first.
1451 uint64_t file_token_lo
= 0;
1452 uint64_t file_token_hi
= 0;
1453 PP_FileHandle file_handle
= OpenNaClExecutable(instance
,
1457 if (file_handle
!= PP_kInvalidFileHandle
) {
1458 PP_NaClFileInfo file_info
;
1459 file_info
.handle
= file_handle
;
1460 file_info
.token_lo
= file_token_lo
;
1461 file_info
.token_hi
= file_token_hi
;
1462 base::MessageLoop::current()->PostTask(
1464 base::Bind(callback
, static_cast<int32_t>(PP_OK
), file_info
));
1468 // The fast path didn't work, we'll fetch the file using URLLoader and write
1469 // it to local storage.
1470 base::File
target_file(CreateTemporaryFile(instance
));
1473 content::PepperPluginInstance
* plugin_instance
=
1474 content::PepperPluginInstance::Get(instance
);
1475 if (!plugin_instance
) {
1476 base::MessageLoop::current()->PostTask(
1478 base::Bind(callback
,
1479 static_cast<int32_t>(PP_ERROR_FAILED
),
1480 kInvalidNaClFileInfo
));
1482 const blink::WebDocument
& document
=
1483 plugin_instance
->GetContainer()->element().document();
1484 scoped_ptr
<blink::WebURLLoader
> url_loader(
1485 CreateWebURLLoader(document
, gurl
));
1486 blink::WebURLRequest url_request
= CreateWebURLRequest(document
, gurl
);
1488 ProgressEventRateLimiter
* tracker
= new ProgressEventRateLimiter(instance
);
1490 // FileDownloader deletes itself after invoking DownloadNexeCompletion.
1491 FileDownloader
* file_downloader
= new FileDownloader(
1494 base::Bind(&DownloadFileCompletion
, callback
),
1495 base::Bind(&ProgressEventRateLimiter::ReportProgress
,
1496 base::Owned(tracker
), std::string(url
)));
1497 file_downloader
->Load(url_request
);
1500 void ReportSelLdrStatus(PP_Instance instance
,
1501 int32_t load_status
,
1502 int32_t max_status
) {
1503 HistogramEnumerate("NaCl.LoadStatus.SelLdr", load_status
, max_status
);
1504 NexeLoadManager
* load_manager
= GetNexeLoadManager(instance
);
1505 DCHECK(load_manager
);
1509 // Gather data to see if being installed changes load outcomes.
1510 const char* name
= load_manager
->is_installed() ?
1511 "NaCl.LoadStatus.SelLdr.InstalledApp" :
1512 "NaCl.LoadStatus.SelLdr.NotInstalledApp";
1513 HistogramEnumerate(name
, load_status
, max_status
);
1516 void LogTranslateTime(const char* histogram_name
,
1517 int64_t time_in_us
) {
1518 ppapi::PpapiGlobals::Get()->GetMainThreadMessageLoop()->PostTask(
1520 base::Bind(&HistogramTimeTranslation
,
1521 std::string(histogram_name
),
1522 time_in_us
/ 1000));
1525 void LogBytesCompiledVsDowloaded(PP_Bool use_subzero
,
1526 int64_t pexe_bytes_compiled
,
1527 int64_t pexe_bytes_downloaded
) {
1528 HistogramRatio("NaCl.Perf.PNaClLoadTime.PctCompiledWhenFullyDownloaded",
1529 pexe_bytes_compiled
, pexe_bytes_downloaded
);
1532 ? "NaCl.Perf.PNaClLoadTime.PctCompiledWhenFullyDownloaded.Subzero"
1533 : "NaCl.Perf.PNaClLoadTime.PctCompiledWhenFullyDownloaded.LLC",
1534 pexe_bytes_compiled
, pexe_bytes_downloaded
);
1537 void SetPNaClStartTime(PP_Instance instance
) {
1538 NexeLoadManager
* load_manager
= GetNexeLoadManager(instance
);
1540 load_manager
->set_pnacl_start_time(base::Time::Now());
1543 // PexeDownloader is responsible for deleting itself when the download
1545 class PexeDownloader
: public blink::WebURLLoaderClient
{
1547 PexeDownloader(PP_Instance instance
,
1548 scoped_ptr
<blink::WebURLLoader
> url_loader
,
1549 const std::string
& pexe_url
,
1550 int32_t pexe_opt_level
,
1552 const PPP_PexeStreamHandler
* stream_handler
,
1553 void* stream_handler_user_data
)
1554 : instance_(instance
),
1555 url_loader_(url_loader
.Pass()),
1556 pexe_url_(pexe_url
),
1557 pexe_opt_level_(pexe_opt_level
),
1558 use_subzero_(use_subzero
),
1559 stream_handler_(stream_handler
),
1560 stream_handler_user_data_(stream_handler_user_data
),
1562 expected_content_length_(-1),
1563 weak_factory_(this) {}
1565 void Load(const blink::WebURLRequest
& request
) {
1566 url_loader_
->loadAsynchronously(request
, this);
1570 virtual void didReceiveResponse(blink::WebURLLoader
* loader
,
1571 const blink::WebURLResponse
& response
) {
1572 success_
= (response
.httpStatusCode() == 200);
1576 expected_content_length_
= response
.expectedContentLength();
1578 // Defer loading after receiving headers. This is because we may already
1579 // have a cached translated nexe, so check for that now.
1580 url_loader_
->setDefersLoading(true);
1582 std::string etag
= response
.httpHeaderField("etag").utf8();
1583 std::string last_modified
=
1584 response
.httpHeaderField("last-modified").utf8();
1585 base::Time last_modified_time
;
1586 base::Time::FromString(last_modified
.c_str(), &last_modified_time
);
1588 bool has_no_store_header
= false;
1589 std::string cache_control
=
1590 response
.httpHeaderField("cache-control").utf8();
1592 std::vector
<std::string
> values
;
1593 base::SplitString(cache_control
, ',', &values
);
1594 for (std::vector
<std::string
>::const_iterator it
= values
.begin();
1597 if (base::StringToLowerASCII(*it
) == "no-store")
1598 has_no_store_header
= true;
1602 instance_
, pexe_url_
, pexe_opt_level_
, last_modified_time
, etag
,
1603 has_no_store_header
, use_subzero_
,
1604 base::Bind(&PexeDownloader::didGetNexeFd
, weak_factory_
.GetWeakPtr()));
1607 virtual void didGetNexeFd(int32_t pp_error
,
1609 PP_FileHandle file_handle
) {
1610 if (!content::PepperPluginInstance::Get(instance_
)) {
1615 HistogramEnumerate("NaCl.Perf.PNaClCache.IsHit", cache_hit
, 2);
1616 HistogramEnumerate(use_subzero_
? "NaCl.Perf.PNaClCache.IsHit.Subzero"
1617 : "NaCl.Perf.PNaClCache.IsHit.LLC",
1620 stream_handler_
->DidCacheHit(stream_handler_user_data_
, file_handle
);
1622 // We delete the PexeDownloader at this point since we successfully got a
1623 // cached, translated nexe.
1627 stream_handler_
->DidCacheMiss(stream_handler_user_data_
,
1628 expected_content_length_
,
1631 // No translated nexe was found in the cache, so we should download the
1632 // file to start streaming it.
1633 url_loader_
->setDefersLoading(false);
1636 virtual void didReceiveData(blink::WebURLLoader
* loader
,
1639 int encoded_data_length
) {
1640 if (content::PepperPluginInstance::Get(instance_
)) {
1641 // Stream the data we received to the stream callback.
1642 stream_handler_
->DidStreamData(stream_handler_user_data_
,
1648 virtual void didFinishLoading(blink::WebURLLoader
* loader
,
1650 int64_t total_encoded_data_length
) {
1651 int32_t result
= success_
? PP_OK
: PP_ERROR_FAILED
;
1653 if (content::PepperPluginInstance::Get(instance_
))
1654 stream_handler_
->DidFinishStream(stream_handler_user_data_
, result
);
1658 virtual void didFail(blink::WebURLLoader
* loader
,
1659 const blink::WebURLError
& error
) {
1663 PP_Instance instance_
;
1664 scoped_ptr
<blink::WebURLLoader
> url_loader_
;
1665 std::string pexe_url_
;
1666 int32_t pexe_opt_level_
;
1668 const PPP_PexeStreamHandler
* stream_handler_
;
1669 void* stream_handler_user_data_
;
1671 int64_t expected_content_length_
;
1672 base::WeakPtrFactory
<PexeDownloader
> weak_factory_
;
1675 void StreamPexe(PP_Instance instance
,
1676 const char* pexe_url
,
1678 PP_Bool use_subzero
,
1679 const PPP_PexeStreamHandler
* handler
,
1680 void* handler_user_data
) {
1681 content::PepperPluginInstance
* plugin_instance
=
1682 content::PepperPluginInstance::Get(instance
);
1683 if (!plugin_instance
) {
1684 base::MessageLoop::current()->PostTask(
1686 base::Bind(handler
->DidFinishStream
,
1688 static_cast<int32_t>(PP_ERROR_FAILED
)));
1692 GURL
gurl(pexe_url
);
1693 const blink::WebDocument
& document
=
1694 plugin_instance
->GetContainer()->element().document();
1695 scoped_ptr
<blink::WebURLLoader
> url_loader(
1696 CreateWebURLLoader(document
, gurl
));
1697 PexeDownloader
* downloader
=
1698 new PexeDownloader(instance
, url_loader
.Pass(), pexe_url
, opt_level
,
1699 PP_ToBool(use_subzero
), handler
, handler_user_data
);
1701 blink::WebURLRequest url_request
= CreateWebURLRequest(document
, gurl
);
1702 // Mark the request as requesting a PNaCl bitcode file,
1703 // so that component updater can detect this user action.
1704 url_request
.addHTTPHeaderField(
1705 blink::WebString::fromUTF8("Accept"),
1706 blink::WebString::fromUTF8("application/x-pnacl, */*"));
1707 url_request
.setRequestContext(blink::WebURLRequest::RequestContextObject
);
1708 downloader
->Load(url_request
);
1711 const PPB_NaCl_Private nacl_interface
= {
1714 &BrokerDuplicateHandle
,
1715 &GetReadExecPnaclFd
,
1716 &CreateTemporaryFile
,
1717 &GetNumberOfProcessors
,
1718 &ReportTranslationFinished
,
1726 &RequestNaClManifest
,
1727 &GetManifestBaseURL
,
1728 &ProcessNaClManifest
,
1729 &ManifestGetProgramURL
,
1730 &GetPNaClResourceInfo
,
1731 &GetCpuFeatureAttrs
,
1733 &ReportSelLdrStatus
,
1735 &LogBytesCompiledVsDowloaded
,
1742 const PPB_NaCl_Private
* GetNaClPrivateInterface() {
1743 return &nacl_interface
;