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()) {
85 g_pnacl_resource_host.Get() = new PnaclTranslationResourceHost(
86 render_thread->GetIOMessageLoopProxy());
87 render_thread->AddFilter(g_pnacl_resource_host.Get());
93 InstanceInfo() : plugin_pid(base::kNullProcessId), plugin_child_id(0) {}
95 ppapi::PpapiPermissions permissions;
96 base::ProcessId plugin_pid;
98 IPC::ChannelHandle channel_handle;
101 typedef std::map<PP_Instance, InstanceInfo> InstanceInfoMap;
103 base::LazyInstance<InstanceInfoMap> g_instance_info =
104 LAZY_INSTANCE_INITIALIZER;
106 typedef base::ScopedPtrHashMap<PP_Instance, NexeLoadManager>
109 base::LazyInstance<NexeLoadManagerMap> g_load_manager_map =
110 LAZY_INSTANCE_INITIALIZER;
112 nacl::NexeLoadManager* GetNexeLoadManager(PP_Instance instance) {
113 NexeLoadManagerMap& map = g_load_manager_map.Get();
114 NexeLoadManagerMap::iterator iter = map.find(instance);
115 if (iter != map.end())
120 static const PP_NaClFileInfo kInvalidNaClFileInfo = {
121 PP_kInvalidFileHandle,
126 int GetRoutingID(PP_Instance instance) {
127 // Check that we are on the main renderer thread.
128 DCHECK(content::RenderThread::Get());
129 content::RendererPpapiHost *host =
130 content::RendererPpapiHost::GetForPPInstance(instance);
133 return host->GetRoutingIDForWidget(instance);
136 // Returns whether the channel_handle is valid or not.
137 bool IsValidChannelHandle(const IPC::ChannelHandle& channel_handle) {
138 if (channel_handle.name.empty()) {
142 #if defined(OS_POSIX)
143 if (channel_handle.socket.fd == -1) {
151 void PostPPCompletionCallback(PP_CompletionCallback callback,
153 ppapi::PpapiGlobals::Get()->GetMainThreadMessageLoop()->PostTask(
155 base::Bind(callback.func, callback.user_data, status));
158 bool ManifestResolveKey(PP_Instance instance,
159 bool is_helper_process,
160 const std::string& key,
161 std::string* full_url,
162 PP_PNaClOptions* pnacl_options);
164 typedef base::Callback<void(int32_t, const PP_NaClFileInfo&)>
165 DownloadFileCallback;
167 void DownloadFile(PP_Instance instance,
168 const std::string& url,
169 const DownloadFileCallback& callback);
171 PP_Bool StartPpapiProxy(PP_Instance instance);
173 // Thin adapter from PPP_ManifestService to ManifestServiceChannel::Delegate.
174 // Note that user_data is managed by the caller of LaunchSelLdr. Please see
175 // also PP_ManifestService's comment for more details about resource
177 class ManifestServiceProxy : public ManifestServiceChannel::Delegate {
179 ManifestServiceProxy(PP_Instance pp_instance)
180 : pp_instance_(pp_instance) {
183 virtual ~ManifestServiceProxy() { }
185 virtual void StartupInitializationComplete() OVERRIDE {
186 if (StartPpapiProxy(pp_instance_) == PP_TRUE) {
187 JsonManifest* manifest = GetJsonManifest(pp_instance_);
188 NexeLoadManager* load_manager = GetNexeLoadManager(pp_instance_);
189 if (load_manager && manifest) {
190 std::string full_url;
191 PP_PNaClOptions pnacl_options;
192 bool uses_nonsfi_mode;
193 JsonManifest::ErrorInfo error_info;
194 if (manifest->GetProgramURL(&full_url,
198 int64_t nexe_size = load_manager->nexe_size();
199 load_manager->ReportLoadSuccess(full_url, nexe_size, nexe_size);
205 virtual void OpenResource(
206 const std::string& key,
207 const ManifestServiceChannel::OpenResourceCallback& callback) OVERRIDE {
208 DCHECK(ppapi::PpapiGlobals::Get()->GetMainThreadMessageLoop()->
209 BelongsToCurrentThread());
212 // TODO(teravest): Clean up pnacl_options logic in JsonManifest so we don't
213 // have to initialize it like this here.
214 PP_PNaClOptions pnacl_options;
215 pnacl_options.translate = PP_FALSE;
216 pnacl_options.is_debug = PP_FALSE;
217 pnacl_options.opt_level = 2;
218 if (!ManifestResolveKey(pp_instance_, false, key, &url, &pnacl_options)) {
219 base::MessageLoop::current()->PostTask(
221 base::Bind(callback, base::Passed(base::File())));
225 // We have to call DidDownloadFile, even if this object is destroyed, so
226 // that the handle inside PP_NaClFileInfo isn't leaked. This means that the
227 // callback passed to this function shouldn't have a weak pointer to an
230 // TODO(teravest): Make a type like PP_NaClFileInfo to use for DownloadFile
231 // that would close the file handle on destruction.
232 DownloadFile(pp_instance_, url,
233 base::Bind(&ManifestServiceProxy::DidDownloadFile, callback));
237 static void DidDownloadFile(
238 ManifestServiceChannel::OpenResourceCallback callback,
240 const PP_NaClFileInfo& file_info) {
241 if (pp_error != PP_OK) {
242 callback.Run(base::File());
245 callback.Run(base::File(file_info.handle));
248 PP_Instance pp_instance_;
249 DISALLOW_COPY_AND_ASSIGN(ManifestServiceProxy);
252 blink::WebURLLoader* CreateWebURLLoader(const blink::WebDocument& document,
254 blink::WebURLLoaderOptions options;
255 options.untrustedHTTP = true;
257 // Options settings here follow the original behavior in the trusted
258 // plugin and PepperURLLoaderHost.
259 if (document.securityOrigin().canRequest(gurl)) {
260 options.allowCredentials = true;
263 options.crossOriginRequestPolicy =
264 blink::WebURLLoaderOptions::CrossOriginRequestPolicyUseAccessControl;
266 return document.frame()->createAssociatedURLLoader(options);
269 blink::WebURLRequest CreateWebURLRequest(const blink::WebDocument& document,
271 blink::WebURLRequest request;
272 request.initialize();
273 request.setURL(gurl);
274 request.setFirstPartyForCookies(document.firstPartyForCookies());
278 int32_t FileDownloaderToPepperError(FileDownloader::Status status) {
280 case FileDownloader::SUCCESS:
282 case FileDownloader::ACCESS_DENIED:
283 return PP_ERROR_NOACCESS;
284 case FileDownloader::FAILED:
285 return PP_ERROR_FAILED;
286 // No default case, to catch unhandled Status values.
288 return PP_ERROR_FAILED;
291 // Launch NaCl's sel_ldr process.
292 void LaunchSelLdr(PP_Instance instance,
293 PP_Bool main_service_runtime,
294 const char* alleged_url,
295 const PP_NaClFileInfo* nexe_file_info,
298 PP_Bool uses_nonsfi_mode,
299 PP_Bool enable_ppapi_dev,
300 PP_Bool enable_dyncode_syscalls,
301 PP_Bool enable_exception_handling,
302 PP_Bool enable_crash_throttling,
304 PP_CompletionCallback callback) {
305 CHECK(ppapi::PpapiGlobals::Get()->GetMainThreadMessageLoop()->
306 BelongsToCurrentThread());
308 // Create the manifest service proxy here, so on error case, it will be
309 // destructed (without passing it to ManifestServiceChannel).
310 scoped_ptr<ManifestServiceChannel::Delegate> manifest_service_proxy(
311 new ManifestServiceProxy(instance));
313 FileDescriptor result_socket;
314 IPC::Sender* sender = content::RenderThread::Get();
317 // If the nexe uses ppapi APIs, we need a routing ID.
318 // To get the routing ID, we must be on the main thread.
319 // Some nexes do not use ppapi and launch from the background thread,
320 // so those nexes can skip finding a routing_id.
322 routing_id = GetRoutingID(instance);
324 if (nexe_file_info->handle != PP_kInvalidFileHandle) {
325 base::File closer(nexe_file_info->handle);
327 ppapi::PpapiGlobals::Get()->GetMainThreadMessageLoop()->PostTask(
329 base::Bind(callback.func, callback.user_data,
330 static_cast<int32_t>(PP_ERROR_FAILED)));
335 InstanceInfo instance_info;
336 instance_info.url = GURL(alleged_url);
338 uint32_t perm_bits = ppapi::PERMISSION_NONE;
339 // Conditionally block 'Dev' interfaces. We do this for the NaCl process, so
340 // it's clearer to developers when they are using 'Dev' inappropriately. We
341 // must also check on the trusted side of the proxy.
342 if (enable_ppapi_dev)
343 perm_bits |= ppapi::PERMISSION_DEV;
344 instance_info.permissions =
345 ppapi::PpapiPermissions::GetForCommandLine(perm_bits);
346 std::string error_message_string;
347 NaClLaunchResult launch_result;
349 IPC::PlatformFileForTransit nexe_for_transit =
350 IPC::InvalidPlatformFileForTransit();
351 #if defined(OS_POSIX)
352 if (nexe_file_info->handle != PP_kInvalidFileHandle)
353 nexe_for_transit = base::FileDescriptor(nexe_file_info->handle, true);
354 #elif defined(OS_WIN)
355 // Duplicate the handle on the browser side instead of the renderer.
356 // This is because BrokerGetFileForProcess isn't part of content/public, and
357 // it's simpler to do the duplication in the browser anyway.
358 nexe_for_transit = nexe_file_info->handle;
360 #error Unsupported target platform.
362 if (!sender->Send(new NaClHostMsg_LaunchNaCl(
364 instance_info.url.spec(),
366 nexe_file_info->token_lo,
367 nexe_file_info->token_hi,
371 PP_ToBool(uses_nonsfi_mode),
372 PP_ToBool(enable_dyncode_syscalls),
373 PP_ToBool(enable_exception_handling),
374 PP_ToBool(enable_crash_throttling)),
376 &error_message_string))) {
377 ppapi::PpapiGlobals::Get()->GetMainThreadMessageLoop()->PostTask(
379 base::Bind(callback.func, callback.user_data,
380 static_cast<int32_t>(PP_ERROR_FAILED)));
384 if (!error_message_string.empty()) {
385 if (PP_ToBool(main_service_runtime)) {
386 NexeLoadManager* load_manager = GetNexeLoadManager(instance);
388 load_manager->ReportLoadError(PP_NACL_ERROR_SEL_LDR_LAUNCH,
389 "ServiceRuntime: failed to start",
390 error_message_string);
393 ppapi::PpapiGlobals::Get()->GetMainThreadMessageLoop()->PostTask(
395 base::Bind(callback.func, callback.user_data,
396 static_cast<int32_t>(PP_ERROR_FAILED)));
399 result_socket = launch_result.imc_channel_handle;
400 instance_info.channel_handle = launch_result.ppapi_ipc_channel_handle;
401 instance_info.plugin_pid = launch_result.plugin_pid;
402 instance_info.plugin_child_id = launch_result.plugin_child_id;
404 // Don't save instance_info if channel handle is invalid.
405 if (IsValidChannelHandle(instance_info.channel_handle))
406 g_instance_info.Get()[instance] = instance_info;
408 *(static_cast<NaClHandle*>(imc_handle)) = ToNativeHandle(result_socket);
410 NexeLoadManager* load_manager = GetNexeLoadManager(instance);
411 DCHECK(load_manager);
413 PostPPCompletionCallback(callback, PP_ERROR_FAILED);
417 // Create the trusted plugin channel.
418 if (IsValidChannelHandle(launch_result.trusted_ipc_channel_handle)) {
419 scoped_ptr<TrustedPluginChannel> trusted_plugin_channel(
420 new TrustedPluginChannel(
421 launch_result.trusted_ipc_channel_handle));
422 load_manager->set_trusted_plugin_channel(trusted_plugin_channel.Pass());
424 PostPPCompletionCallback(callback, PP_ERROR_FAILED);
428 // Create the manifest service handle as well.
429 // For security hardening, disable the IPCs for open_resource() when they
430 // aren't needed. PNaCl doesn't expose open_resource(), and the new
431 // open_resource() IPCs are currently only used for Non-SFI NaCl so far,
432 // not SFI NaCl. Note that enable_dyncode_syscalls is true if and only if
433 // the plugin is a non-PNaCl plugin.
435 enable_dyncode_syscalls &&
437 IsValidChannelHandle(
438 launch_result.manifest_service_ipc_channel_handle)) {
439 scoped_ptr<ManifestServiceChannel> manifest_service_channel(
440 new ManifestServiceChannel(
441 launch_result.manifest_service_ipc_channel_handle,
442 base::Bind(&PostPPCompletionCallback, callback),
443 manifest_service_proxy.Pass(),
444 content::RenderThread::Get()->GetShutdownEvent()));
445 load_manager->set_manifest_service_channel(
446 manifest_service_channel.Pass());
448 // Currently, manifest service works only on linux/non-SFI mode.
449 // On other platforms, the socket will not be created, and thus this
450 // condition needs to be handled as success.
451 PostPPCompletionCallback(callback, PP_OK);
455 PP_Bool StartPpapiProxy(PP_Instance instance) {
456 NexeLoadManager* load_manager = GetNexeLoadManager(instance);
457 DCHECK(load_manager);
461 content::PepperPluginInstance* plugin_instance =
462 content::PepperPluginInstance::Get(instance);
463 if (!plugin_instance) {
464 DLOG(ERROR) << "GetInstance() failed";
468 InstanceInfoMap& map = g_instance_info.Get();
469 InstanceInfoMap::iterator it = map.find(instance);
470 if (it == map.end()) {
471 DLOG(ERROR) << "Could not find instance ID";
474 InstanceInfo instance_info = it->second;
477 PP_ExternalPluginResult result = plugin_instance->SwitchToOutOfProcessProxy(
478 base::FilePath().AppendASCII(instance_info.url.spec()),
479 instance_info.permissions,
480 instance_info.channel_handle,
481 instance_info.plugin_pid,
482 instance_info.plugin_child_id);
484 if (result == PP_EXTERNAL_PLUGIN_OK) {
485 // Log the amound of time that has passed between the trusted plugin being
486 // initialized and the untrusted plugin being initialized. This is
487 // (roughly) the cost of using NaCl, in terms of startup time.
488 load_manager->ReportStartupOverhead();
490 } else if (result == PP_EXTERNAL_PLUGIN_ERROR_MODULE) {
491 load_manager->ReportLoadError(PP_NACL_ERROR_START_PROXY_MODULE,
492 "could not initialize module.");
493 } else if (result == PP_EXTERNAL_PLUGIN_ERROR_INSTANCE) {
494 load_manager->ReportLoadError(PP_NACL_ERROR_START_PROXY_MODULE,
495 "could not create instance.");
500 int UrandomFD(void) {
501 #if defined(OS_POSIX)
502 return base::GetUrandomFD();
508 PP_Bool Are3DInterfacesDisabled() {
509 return PP_FromBool(CommandLine::ForCurrentProcess()->HasSwitch(
510 switches::kDisable3DAPIs));
513 int32_t BrokerDuplicateHandle(PP_FileHandle source_handle,
515 PP_FileHandle* target_handle,
516 uint32_t desired_access,
519 return content::BrokerDuplicateHandle(source_handle, process_id,
520 target_handle, desired_access,
527 // Convert a URL to a filename for GetReadonlyPnaclFd.
528 // Must be kept in sync with PnaclCanOpenFile() in
529 // components/nacl/browser/nacl_file_host.cc.
530 std::string PnaclComponentURLToFilename(const std::string& url) {
531 // PNaCl component URLs aren't arbitrary URLs; they are always either
532 // generated from ManifestResolveKey or PnaclResources::ReadResourceInfo.
533 // So, it's safe to just use string parsing operations here instead of
535 DCHECK(StartsWithASCII(url, kPNaClTranslatorBaseUrl, true));
536 std::string r = url.substr(std::string(kPNaClTranslatorBaseUrl).length());
538 // Use white-listed-chars.
540 static const char* white_list = "abcdefghijklmnopqrstuvwxyz0123456789_";
541 replace_pos = r.find_first_not_of(white_list);
542 while(replace_pos != std::string::npos) {
543 r = r.replace(replace_pos, 1, "_");
544 replace_pos = r.find_first_not_of(white_list);
549 PP_FileHandle GetReadonlyPnaclFd(const char* url,
552 uint64_t* nonce_hi) {
553 std::string filename = PnaclComponentURLToFilename(url);
554 IPC::PlatformFileForTransit out_fd = IPC::InvalidPlatformFileForTransit();
555 IPC::Sender* sender = content::RenderThread::Get();
557 if (!sender->Send(new NaClHostMsg_GetReadonlyPnaclFD(
558 std::string(filename), is_executable,
559 &out_fd, nonce_lo, nonce_hi))) {
560 return PP_kInvalidFileHandle;
562 if (out_fd == IPC::InvalidPlatformFileForTransit()) {
563 return PP_kInvalidFileHandle;
565 return IPC::PlatformFileForTransitToPlatformFile(out_fd);
568 void GetReadExecPnaclFd(const char* url,
569 PP_NaClFileInfo* out_file_info) {
570 *out_file_info = kInvalidNaClFileInfo;
571 out_file_info->handle = GetReadonlyPnaclFd(url, true /* is_executable */,
572 &out_file_info->token_lo,
573 &out_file_info->token_hi);
576 PP_FileHandle CreateTemporaryFile(PP_Instance instance) {
577 IPC::PlatformFileForTransit transit_fd = IPC::InvalidPlatformFileForTransit();
578 IPC::Sender* sender = content::RenderThread::Get();
580 if (!sender->Send(new NaClHostMsg_NaClCreateTemporaryFile(
582 return PP_kInvalidFileHandle;
585 if (transit_fd == IPC::InvalidPlatformFileForTransit()) {
586 return PP_kInvalidFileHandle;
589 return IPC::PlatformFileForTransitToPlatformFile(transit_fd);
592 int32_t GetNumberOfProcessors() {
593 IPC::Sender* sender = content::RenderThread::Get();
595 int32_t num_processors = 1;
596 return sender->Send(new NaClHostMsg_NaClGetNumProcessors(&num_processors)) ?
600 PP_Bool PPIsNonSFIModeEnabled() {
601 return PP_FromBool(IsNonSFIModeEnabled());
604 void GetNexeFd(PP_Instance instance,
605 const std::string& pexe_url,
607 const base::Time& last_modified_time,
608 const std::string& etag,
609 bool has_no_store_header,
610 base::Callback<void(int32_t, bool, PP_FileHandle)> callback) {
611 if (!InitializePnaclResourceHost()) {
612 ppapi::PpapiGlobals::Get()->GetMainThreadMessageLoop()->PostTask(
615 static_cast<int32_t>(PP_ERROR_FAILED),
617 PP_kInvalidFileHandle));
621 PnaclCacheInfo cache_info;
622 cache_info.pexe_url = GURL(pexe_url);
623 // TODO(dschuff): Get this value from the pnacl json file after it
624 // rolls in from NaCl.
625 cache_info.abi_version = 1;
626 cache_info.opt_level = opt_level;
627 cache_info.last_modified = last_modified_time;
628 cache_info.etag = etag;
629 cache_info.has_no_store_header = has_no_store_header;
630 cache_info.sandbox_isa = GetSandboxArch();
631 cache_info.extra_flags = GetCpuFeatures();
633 g_pnacl_resource_host.Get()->RequestNexeFd(
634 GetRoutingID(instance),
640 void ReportTranslationFinished(PP_Instance instance,
644 int64_t compile_time_us) {
645 if (success == PP_TRUE) {
646 static const int32_t kUnknownOptLevel = 4;
647 if (opt_level < 0 || opt_level > 3)
648 opt_level = kUnknownOptLevel;
649 HistogramEnumerate("NaCl.Options.PNaCl.OptLevel",
651 kUnknownOptLevel + 1);
652 HistogramKBPerSec("NaCl.Perf.PNaClLoadTime.CompileKBPerSec",
655 HistogramSizeKB("NaCl.Perf.Size.Pexe", pexe_size / 1024);
657 NexeLoadManager* load_manager = GetNexeLoadManager(instance);
659 base::TimeDelta total_time = base::Time::Now() -
660 load_manager->pnacl_start_time();
661 HistogramTimeTranslation("NaCl.Perf.PNaClLoadTime.TotalUncachedTime",
662 total_time.InMilliseconds());
663 HistogramKBPerSec("NaCl.Perf.PNaClLoadTime.TotalUncachedKBPerSec",
665 total_time.InMicroseconds());
669 // If the resource host isn't initialized, don't try to do that here.
670 // Just return because something is already very wrong.
671 if (g_pnacl_resource_host.Get() == NULL)
673 g_pnacl_resource_host.Get()->ReportTranslationFinished(instance, success);
676 PP_FileHandle OpenNaClExecutable(PP_Instance instance,
677 const char* file_url,
679 uint64_t* nonce_hi) {
680 // Fast path only works for installed file URLs.
682 if (!gurl.SchemeIs("chrome-extension"))
683 return PP_kInvalidFileHandle;
685 content::PepperPluginInstance* plugin_instance =
686 content::PepperPluginInstance::Get(instance);
687 if (!plugin_instance)
688 return PP_kInvalidFileHandle;
689 // IMPORTANT: Make sure the document can request the given URL. If we don't
690 // check, a malicious app could probe the extension system. This enforces a
691 // same-origin policy which prevents the app from requesting resources from
693 blink::WebSecurityOrigin security_origin =
694 plugin_instance->GetContainer()->element().document().securityOrigin();
695 if (!security_origin.canRequest(gurl))
696 return PP_kInvalidFileHandle;
698 IPC::PlatformFileForTransit out_fd = IPC::InvalidPlatformFileForTransit();
699 IPC::Sender* sender = content::RenderThread::Get();
703 base::FilePath file_path;
705 new NaClHostMsg_OpenNaClExecutable(GetRoutingID(instance),
710 return PP_kInvalidFileHandle;
713 if (out_fd == IPC::InvalidPlatformFileForTransit())
714 return PP_kInvalidFileHandle;
716 return IPC::PlatformFileForTransitToPlatformFile(out_fd);
719 void DispatchEvent(PP_Instance instance,
720 PP_NaClEventType event_type,
721 const char *resource_url,
722 PP_Bool length_is_computable,
723 uint64_t loaded_bytes,
724 uint64_t total_bytes) {
725 ProgressEvent event(event_type,
727 PP_ToBool(length_is_computable),
730 DispatchProgressEvent(instance, event);
733 void ReportLoadSuccess(PP_Instance instance,
734 uint64_t loaded_bytes,
735 uint64_t total_bytes) {
736 NexeLoadManager* load_manager = GetNexeLoadManager(instance);
738 load_manager->ReportLoadSuccess(load_manager->program_url(),
744 void ReportLoadError(PP_Instance instance,
746 const char* error_message) {
747 NexeLoadManager* load_manager = GetNexeLoadManager(instance);
749 load_manager->ReportLoadError(error, error_message);
752 void ReportLoadAbort(PP_Instance instance) {
753 NexeLoadManager* load_manager = GetNexeLoadManager(instance);
755 load_manager->ReportLoadAbort();
758 void NexeDidCrash(PP_Instance instance, const char* crash_log) {
759 NexeLoadManager* load_manager = GetNexeLoadManager(instance);
761 load_manager->NexeDidCrash(crash_log);
764 void InstanceCreated(PP_Instance instance) {
765 scoped_ptr<NexeLoadManager> new_load_manager(new NexeLoadManager(instance));
766 NexeLoadManagerMap& map = g_load_manager_map.Get();
767 DLOG_IF(ERROR, map.count(instance) != 0) << "Instance count should be 0";
768 map.add(instance, new_load_manager.Pass());
771 void InstanceDestroyed(PP_Instance instance) {
772 DeleteJsonManifest(instance);
774 NexeLoadManagerMap& map = g_load_manager_map.Get();
775 DLOG_IF(ERROR, map.count(instance) == 0) << "Could not find instance ID";
776 // The erase may call NexeLoadManager's destructor prior to removing it from
777 // the map. In that case, it is possible for the trusted Plugin to re-enter
778 // the NexeLoadManager (e.g., by calling ReportLoadError). Passing out the
779 // NexeLoadManager to a local scoped_ptr just ensures that its entry is gone
780 // from the map prior to the destructor being invoked.
781 scoped_ptr<NexeLoadManager> temp(map.take(instance));
785 PP_Bool NaClDebugEnabledForURL(const char* alleged_nmf_url) {
786 if (!CommandLine::ForCurrentProcess()->HasSwitch(switches::kEnableNaClDebug))
788 IPC::Sender* sender = content::RenderThread::Get();
790 bool should_debug = false;
792 sender->Send(new NaClHostMsg_NaClDebugEnabledForURL(GURL(alleged_nmf_url),
797 void LogToConsole(PP_Instance instance, const char* message) {
798 NexeLoadManager* load_manager = GetNexeLoadManager(instance);
799 DCHECK(load_manager);
801 load_manager->LogToConsole(std::string(message));
804 PP_NaClReadyState GetNaClReadyState(PP_Instance instance) {
805 NexeLoadManager* load_manager = GetNexeLoadManager(instance);
806 DCHECK(load_manager);
808 return load_manager->nacl_ready_state();
809 return PP_NACL_READY_STATE_UNSENT;
812 int32_t GetExitStatus(PP_Instance instance) {
813 NexeLoadManager* load_manager = GetNexeLoadManager(instance);
814 DCHECK(load_manager);
816 return load_manager->exit_status();
820 void SetExitStatus(PP_Instance instance, int32_t exit_status) {
821 NexeLoadManager* load_manager = GetNexeLoadManager(instance);
822 DCHECK(load_manager);
824 return load_manager->set_exit_status(exit_status);
827 void Vlog(const char* message) {
831 void InitializePlugin(PP_Instance instance,
834 const char* argv[]) {
835 NexeLoadManager* load_manager = GetNexeLoadManager(instance);
836 DCHECK(load_manager);
838 load_manager->InitializePlugin(argc, argn, argv);
841 int64_t GetNexeSize(PP_Instance instance) {
842 NexeLoadManager* load_manager = GetNexeLoadManager(instance);
843 DCHECK(load_manager);
845 return load_manager->nexe_size();
849 void DownloadManifestToBuffer(PP_Instance instance,
850 struct PP_CompletionCallback callback);
852 bool CreateJsonManifest(PP_Instance instance,
853 const std::string& manifest_url,
854 const std::string& manifest_data);
856 void RequestNaClManifest(PP_Instance instance,
857 PP_CompletionCallback callback) {
858 NexeLoadManager* load_manager = GetNexeLoadManager(instance);
859 DCHECK(load_manager);
861 ppapi::PpapiGlobals::Get()->GetMainThreadMessageLoop()->PostTask(
863 base::Bind(callback.func, callback.user_data,
864 static_cast<int32_t>(PP_ERROR_FAILED)));
868 std::string url = load_manager->GetManifestURLArgument();
869 if (url.empty() || !load_manager->RequestNaClManifest(url)) {
870 ppapi::PpapiGlobals::Get()->GetMainThreadMessageLoop()->PostTask(
872 base::Bind(callback.func, callback.user_data,
873 static_cast<int32_t>(PP_ERROR_FAILED)));
877 const GURL& base_url = load_manager->manifest_base_url();
878 if (base_url.SchemeIs("data")) {
880 std::string mime_type;
883 int32_t error = PP_ERROR_FAILED;
884 if (net::DataURL::Parse(gurl, &mime_type, &charset, &data)) {
885 if (data.size() <= ManifestDownloader::kNaClManifestMaxFileBytes) {
886 if (CreateJsonManifest(instance, base_url.spec(), data))
889 load_manager->ReportLoadError(PP_NACL_ERROR_MANIFEST_TOO_LARGE,
890 "manifest file too large.");
893 load_manager->ReportLoadError(PP_NACL_ERROR_MANIFEST_LOAD_URL,
894 "could not load manifest url.");
896 ppapi::PpapiGlobals::Get()->GetMainThreadMessageLoop()->PostTask(
898 base::Bind(callback.func, callback.user_data, error));
900 DownloadManifestToBuffer(instance, callback);
904 PP_Var GetManifestBaseURL(PP_Instance instance) {
905 NexeLoadManager* load_manager = GetNexeLoadManager(instance);
906 DCHECK(load_manager);
908 return PP_MakeUndefined();
909 const GURL& gurl = load_manager->manifest_base_url();
910 if (!gurl.is_valid())
911 return PP_MakeUndefined();
912 return ppapi::StringVar::StringToPPVar(gurl.spec());
915 void ProcessNaClManifest(PP_Instance instance, const char* program_url) {
916 nacl::NexeLoadManager* load_manager = GetNexeLoadManager(instance);
918 load_manager->ProcessNaClManifest(program_url);
921 PP_Bool DevInterfacesEnabled(PP_Instance instance) {
922 nacl::NexeLoadManager* load_manager = GetNexeLoadManager(instance);
924 return PP_FromBool(load_manager->DevInterfacesEnabled());
928 void DownloadManifestToBufferCompletion(PP_Instance instance,
929 struct PP_CompletionCallback callback,
930 base::Time start_time,
931 PP_NaClError pp_nacl_error,
932 const std::string& data);
934 void DownloadManifestToBuffer(PP_Instance instance,
935 struct PP_CompletionCallback callback) {
936 nacl::NexeLoadManager* load_manager = GetNexeLoadManager(instance);
937 DCHECK(load_manager);
938 content::PepperPluginInstance* plugin_instance =
939 content::PepperPluginInstance::Get(instance);
940 if (!load_manager || !plugin_instance) {
941 ppapi::PpapiGlobals::Get()->GetMainThreadMessageLoop()->PostTask(
943 base::Bind(callback.func, callback.user_data,
944 static_cast<int32_t>(PP_ERROR_FAILED)));
946 const blink::WebDocument& document =
947 plugin_instance->GetContainer()->element().document();
949 const GURL& gurl = load_manager->manifest_base_url();
950 scoped_ptr<blink::WebURLLoader> url_loader(
951 CreateWebURLLoader(document, gurl));
952 blink::WebURLRequest request = CreateWebURLRequest(document, gurl);
954 // ManifestDownloader deletes itself after invoking the callback.
955 ManifestDownloader* manifest_downloader = new ManifestDownloader(
957 load_manager->is_installed(),
958 base::Bind(DownloadManifestToBufferCompletion,
959 instance, callback, base::Time::Now()));
960 manifest_downloader->Load(request);
963 void DownloadManifestToBufferCompletion(PP_Instance instance,
964 struct PP_CompletionCallback callback,
965 base::Time start_time,
966 PP_NaClError pp_nacl_error,
967 const std::string& data) {
968 base::TimeDelta download_time = base::Time::Now() - start_time;
969 HistogramTimeSmall("NaCl.Perf.StartupTime.ManifestDownload",
970 download_time.InMilliseconds());
972 nacl::NexeLoadManager* load_manager = GetNexeLoadManager(instance);
974 callback.func(callback.user_data, PP_ERROR_ABORTED);
979 switch (pp_nacl_error) {
980 case PP_NACL_ERROR_LOAD_SUCCESS:
983 case PP_NACL_ERROR_MANIFEST_LOAD_URL:
984 pp_error = PP_ERROR_FAILED;
985 load_manager->ReportLoadError(PP_NACL_ERROR_MANIFEST_LOAD_URL,
986 "could not load manifest url.");
988 case PP_NACL_ERROR_MANIFEST_TOO_LARGE:
989 pp_error = PP_ERROR_FILETOOBIG;
990 load_manager->ReportLoadError(PP_NACL_ERROR_MANIFEST_TOO_LARGE,
991 "manifest file too large.");
993 case PP_NACL_ERROR_MANIFEST_NOACCESS_URL:
994 pp_error = PP_ERROR_NOACCESS;
995 load_manager->ReportLoadError(PP_NACL_ERROR_MANIFEST_NOACCESS_URL,
996 "access to manifest url was denied.");
1000 pp_error = PP_ERROR_FAILED;
1001 load_manager->ReportLoadError(PP_NACL_ERROR_MANIFEST_LOAD_URL,
1002 "could not load manifest url.");
1005 if (pp_error == PP_OK) {
1006 std::string base_url = load_manager->manifest_base_url().spec();
1007 if (!CreateJsonManifest(instance, base_url, data))
1008 pp_error = PP_ERROR_FAILED;
1010 callback.func(callback.user_data, pp_error);
1013 bool CreateJsonManifest(PP_Instance instance,
1014 const std::string& manifest_url,
1015 const std::string& manifest_data) {
1016 HistogramSizeKB("NaCl.Perf.Size.Manifest",
1017 static_cast<int32_t>(manifest_data.length() / 1024));
1019 nacl::NexeLoadManager* load_manager = GetNexeLoadManager(instance);
1023 const char* isa_type;
1024 if (load_manager->IsPNaCl())
1025 isa_type = kPortableArch;
1027 isa_type = GetSandboxArch();
1029 scoped_ptr<nacl::JsonManifest> j(
1030 new nacl::JsonManifest(
1031 manifest_url.c_str(),
1033 IsNonSFIModeEnabled(),
1034 PP_ToBool(NaClDebugEnabledForURL(manifest_url.c_str()))));
1035 JsonManifest::ErrorInfo error_info;
1036 if (j->Init(manifest_data.c_str(), &error_info)) {
1037 AddJsonManifest(instance, j.Pass());
1040 load_manager->ReportLoadError(error_info.error, error_info.string);
1044 PP_Bool ManifestGetProgramURL(PP_Instance instance,
1045 PP_Var* pp_full_url,
1046 PP_PNaClOptions* pnacl_options,
1047 PP_Bool* pp_uses_nonsfi_mode) {
1048 nacl::NexeLoadManager* load_manager = GetNexeLoadManager(instance);
1050 JsonManifest* manifest = GetJsonManifest(instance);
1051 if (manifest == NULL)
1054 bool uses_nonsfi_mode;
1055 std::string full_url;
1056 JsonManifest::ErrorInfo error_info;
1057 if (manifest->GetProgramURL(&full_url, pnacl_options, &uses_nonsfi_mode,
1059 *pp_full_url = ppapi::StringVar::StringToPPVar(full_url);
1060 *pp_uses_nonsfi_mode = PP_FromBool(uses_nonsfi_mode);
1065 load_manager->ReportLoadError(error_info.error, error_info.string);
1069 bool ManifestResolveKey(PP_Instance instance,
1070 bool is_helper_process,
1071 const std::string& key,
1072 std::string* full_url,
1073 PP_PNaClOptions* pnacl_options) {
1074 // For "helper" processes (llc and ld, for PNaCl translation), we resolve
1075 // keys manually as there is no existing .nmf file to parse.
1076 if (is_helper_process) {
1077 pnacl_options->translate = PP_FALSE;
1078 // We can only resolve keys in the files/ namespace.
1079 const std::string kFilesPrefix = "files/";
1080 if (key.find(kFilesPrefix) == std::string::npos) {
1081 nacl::NexeLoadManager* load_manager = GetNexeLoadManager(instance);
1083 load_manager->ReportLoadError(PP_NACL_ERROR_MANIFEST_RESOLVE_URL,
1084 "key did not start with files/");
1087 std::string key_basename = key.substr(kFilesPrefix.length());
1088 *full_url = std::string(kPNaClTranslatorBaseUrl) + GetSandboxArch() + "/" +
1093 JsonManifest* manifest = GetJsonManifest(instance);
1094 if (manifest == NULL)
1097 return manifest->ResolveKey(key, full_url, pnacl_options);
1100 PP_Bool GetPNaClResourceInfo(PP_Instance instance,
1101 PP_Var* llc_tool_name,
1102 PP_Var* ld_tool_name) {
1103 static const char kFilename[] = "chrome://pnacl-translator/pnacl.json";
1104 NexeLoadManager* load_manager = GetNexeLoadManager(instance);
1105 DCHECK(load_manager);
1109 uint64_t nonce_lo = 0;
1110 uint64_t nonce_hi = 0;
1111 base::File file(GetReadonlyPnaclFd(kFilename, false /* is_executable */,
1112 &nonce_lo, &nonce_hi));
1113 if (!file.IsValid()) {
1114 load_manager->ReportLoadError(
1115 PP_NACL_ERROR_PNACL_RESOURCE_FETCH,
1116 "The Portable Native Client (pnacl) component is not "
1117 "installed. Please consult chrome://components for more "
1122 base::File::Info file_info;
1123 if (!file.GetInfo(&file_info)) {
1124 load_manager->ReportLoadError(
1125 PP_NACL_ERROR_PNACL_RESOURCE_FETCH,
1126 std::string("GetPNaClResourceInfo, GetFileInfo failed for: ") +
1131 if (file_info.size > 1 << 20) {
1132 load_manager->ReportLoadError(
1133 PP_NACL_ERROR_PNACL_RESOURCE_FETCH,
1134 std::string("GetPNaClResourceInfo, file too large: ") + kFilename);
1138 scoped_ptr<char[]> buffer(new char[file_info.size + 1]);
1139 if (buffer.get() == NULL) {
1140 load_manager->ReportLoadError(
1141 PP_NACL_ERROR_PNACL_RESOURCE_FETCH,
1142 std::string("GetPNaClResourceInfo, couldn't allocate for: ") +
1147 int rc = file.Read(0, buffer.get(), file_info.size);
1149 load_manager->ReportLoadError(
1150 PP_NACL_ERROR_PNACL_RESOURCE_FETCH,
1151 std::string("GetPNaClResourceInfo, reading failed for: ") + kFilename);
1155 // Null-terminate the bytes we we read from the file.
1156 buffer.get()[rc] = 0;
1158 // Expect the JSON file to contain a top-level object (dictionary).
1159 Json::Reader json_reader;
1160 Json::Value json_data;
1161 if (!json_reader.parse(buffer.get(), json_data)) {
1162 load_manager->ReportLoadError(
1163 PP_NACL_ERROR_PNACL_RESOURCE_FETCH,
1164 std::string("Parsing resource info failed: JSON parse error: ") +
1165 json_reader.getFormattedErrorMessages());
1169 if (!json_data.isObject()) {
1170 load_manager->ReportLoadError(
1171 PP_NACL_ERROR_PNACL_RESOURCE_FETCH,
1172 "Parsing resource info failed: Malformed JSON dictionary");
1176 if (json_data.isMember("pnacl-llc-name")) {
1177 Json::Value json_name = json_data["pnacl-llc-name"];
1178 if (json_name.isString()) {
1179 std::string llc_tool_name_str = json_name.asString();
1180 *llc_tool_name = ppapi::StringVar::StringToPPVar(llc_tool_name_str);
1184 if (json_data.isMember("pnacl-ld-name")) {
1185 Json::Value json_name = json_data["pnacl-ld-name"];
1186 if (json_name.isString()) {
1187 std::string ld_tool_name_str = json_name.asString();
1188 *ld_tool_name = ppapi::StringVar::StringToPPVar(ld_tool_name_str);
1194 PP_Var GetCpuFeatureAttrs() {
1195 return ppapi::StringVar::StringToPPVar(GetCpuFeatures());
1198 void PostMessageToJavaScriptMainThread(PP_Instance instance,
1199 const std::string& message) {
1200 content::PepperPluginInstance* plugin_instance =
1201 content::PepperPluginInstance::Get(instance);
1202 if (plugin_instance) {
1203 PP_Var message_var = ppapi::StringVar::StringToPPVar(message);
1204 plugin_instance->PostMessageToJavaScript(message_var);
1205 ppapi::PpapiGlobals::Get()->GetVarTracker()->ReleaseVar(message_var);
1209 void PostMessageToJavaScript(PP_Instance instance, const char* message) {
1210 ppapi::PpapiGlobals::Get()->GetMainThreadMessageLoop()->PostTask(
1212 base::Bind(&PostMessageToJavaScriptMainThread,
1214 std::string(message)));
1217 // Encapsulates some of the state for a call to DownloadNexe to prevent
1218 // argument lists from getting too long.
1219 struct DownloadNexeRequest {
1220 PP_Instance instance;
1222 PP_CompletionCallback callback;
1223 base::Time start_time;
1226 // A utility class to ensure that we don't send progress events more often than
1227 // every 10ms for a given file.
1228 class ProgressEventRateLimiter {
1230 explicit ProgressEventRateLimiter(PP_Instance instance)
1231 : instance_(instance) { }
1233 void ReportProgress(const std::string& url,
1234 int64_t total_bytes_received,
1235 int64_t total_bytes_to_be_received) {
1236 base::Time now = base::Time::Now();
1237 if (now - last_event_ > base::TimeDelta::FromMilliseconds(10)) {
1238 DispatchProgressEvent(instance_,
1239 ProgressEvent(PP_NACL_EVENT_PROGRESS,
1241 total_bytes_to_be_received >= 0,
1242 total_bytes_received,
1243 total_bytes_to_be_received));
1249 PP_Instance instance_;
1250 base::Time last_event_;
1253 void DownloadNexeCompletion(const DownloadNexeRequest& request,
1254 PP_NaClFileInfo* out_file_info,
1255 FileDownloader::Status status,
1256 base::File target_file,
1259 void DownloadNexe(PP_Instance instance,
1261 PP_NaClFileInfo* out_file_info,
1262 PP_CompletionCallback callback) {
1264 CHECK(out_file_info);
1265 DownloadNexeRequest request;
1266 request.instance = instance;
1268 request.callback = callback;
1269 request.start_time = base::Time::Now();
1271 // Try the fast path for retrieving the file first.
1272 PP_FileHandle handle = OpenNaClExecutable(instance,
1274 &out_file_info->token_lo,
1275 &out_file_info->token_hi);
1276 if (handle != PP_kInvalidFileHandle) {
1277 DownloadNexeCompletion(request,
1279 FileDownloader::SUCCESS,
1285 // The fast path didn't work, we'll fetch the file using URLLoader and write
1286 // it to local storage.
1287 base::File target_file(CreateTemporaryFile(instance));
1290 content::PepperPluginInstance* plugin_instance =
1291 content::PepperPluginInstance::Get(instance);
1292 if (!plugin_instance) {
1293 ppapi::PpapiGlobals::Get()->GetMainThreadMessageLoop()->PostTask(
1295 base::Bind(callback.func, callback.user_data,
1296 static_cast<int32_t>(PP_ERROR_FAILED)));
1298 const blink::WebDocument& document =
1299 plugin_instance->GetContainer()->element().document();
1300 scoped_ptr<blink::WebURLLoader> url_loader(
1301 CreateWebURLLoader(document, gurl));
1302 blink::WebURLRequest url_request = CreateWebURLRequest(document, gurl);
1304 ProgressEventRateLimiter* tracker = new ProgressEventRateLimiter(instance);
1306 // FileDownloader deletes itself after invoking DownloadNexeCompletion.
1307 FileDownloader* file_downloader = new FileDownloader(
1310 base::Bind(&DownloadNexeCompletion, request, out_file_info),
1311 base::Bind(&ProgressEventRateLimiter::ReportProgress,
1312 base::Owned(tracker), std::string(url)));
1313 file_downloader->Load(url_request);
1316 void DownloadNexeCompletion(const DownloadNexeRequest& request,
1317 PP_NaClFileInfo* out_file_info,
1318 FileDownloader::Status status,
1319 base::File target_file,
1321 int32_t pp_error = FileDownloaderToPepperError(status);
1322 int64_t bytes_read = -1;
1323 if (pp_error == PP_OK && target_file.IsValid()) {
1324 base::File::Info info;
1325 if (target_file.GetInfo(&info))
1326 bytes_read = info.size;
1329 if (bytes_read == -1) {
1330 target_file.Close();
1331 pp_error = PP_ERROR_FAILED;
1334 base::TimeDelta download_time = base::Time::Now() - request.start_time;
1336 NexeLoadManager* load_manager = GetNexeLoadManager(request.instance);
1338 load_manager->NexeFileDidOpen(pp_error,
1346 if (pp_error == PP_OK && target_file.IsValid())
1347 out_file_info->handle = target_file.TakePlatformFile();
1349 out_file_info->handle = PP_kInvalidFileHandle;
1351 request.callback.func(request.callback.user_data, pp_error);
1354 void DownloadFileCompletion(
1355 const DownloadFileCallback& callback,
1356 FileDownloader::Status status,
1359 int32_t pp_error = FileDownloaderToPepperError(status);
1360 PP_NaClFileInfo file_info;
1361 if (pp_error == PP_OK) {
1362 file_info.handle = file.TakePlatformFile();
1363 file_info.token_lo = 0;
1364 file_info.token_hi = 0;
1366 file_info = kInvalidNaClFileInfo;
1369 callback.Run(pp_error, file_info);
1372 void DownloadFile(PP_Instance instance,
1373 const std::string& url,
1374 const DownloadFileCallback& callback) {
1375 DCHECK(ppapi::PpapiGlobals::Get()->GetMainThreadMessageLoop()->
1376 BelongsToCurrentThread());
1378 NexeLoadManager* load_manager = GetNexeLoadManager(instance);
1379 DCHECK(load_manager);
1380 if (!load_manager) {
1381 base::MessageLoop::current()->PostTask(
1383 base::Bind(callback,
1384 static_cast<int32_t>(PP_ERROR_FAILED),
1385 kInvalidNaClFileInfo));
1389 // Handle special PNaCl support files which are installed on the user's
1391 if (url.find(kPNaClTranslatorBaseUrl, 0) == 0) {
1392 PP_NaClFileInfo file_info = kInvalidNaClFileInfo;
1393 PP_FileHandle handle = GetReadonlyPnaclFd(url.c_str(),
1394 false /* is_executable */,
1395 &file_info.token_lo,
1396 &file_info.token_hi);
1397 if (handle == PP_kInvalidFileHandle) {
1398 base::MessageLoop::current()->PostTask(
1400 base::Bind(callback,
1401 static_cast<int32_t>(PP_ERROR_FAILED),
1402 kInvalidNaClFileInfo));
1405 file_info.handle = handle;
1406 base::MessageLoop::current()->PostTask(
1408 base::Bind(callback, static_cast<int32_t>(PP_OK), file_info));
1412 // We have to ensure that this url resolves relative to the plugin base url
1413 // before downloading it.
1414 const GURL& test_gurl = load_manager->plugin_base_url().Resolve(url);
1415 if (!test_gurl.is_valid()) {
1416 base::MessageLoop::current()->PostTask(
1418 base::Bind(callback,
1419 static_cast<int32_t>(PP_ERROR_FAILED),
1420 kInvalidNaClFileInfo));
1424 // Try the fast path for retrieving the file first.
1425 uint64_t file_token_lo = 0;
1426 uint64_t file_token_hi = 0;
1427 PP_FileHandle file_handle = OpenNaClExecutable(instance,
1431 if (file_handle != PP_kInvalidFileHandle) {
1432 PP_NaClFileInfo file_info;
1433 file_info.handle = file_handle;
1434 file_info.token_lo = file_token_lo;
1435 file_info.token_hi = file_token_hi;
1436 base::MessageLoop::current()->PostTask(
1438 base::Bind(callback, static_cast<int32_t>(PP_OK), file_info));
1442 // The fast path didn't work, we'll fetch the file using URLLoader and write
1443 // it to local storage.
1444 base::File target_file(CreateTemporaryFile(instance));
1447 content::PepperPluginInstance* plugin_instance =
1448 content::PepperPluginInstance::Get(instance);
1449 if (!plugin_instance) {
1450 base::MessageLoop::current()->PostTask(
1452 base::Bind(callback,
1453 static_cast<int32_t>(PP_ERROR_FAILED),
1454 kInvalidNaClFileInfo));
1456 const blink::WebDocument& document =
1457 plugin_instance->GetContainer()->element().document();
1458 scoped_ptr<blink::WebURLLoader> url_loader(
1459 CreateWebURLLoader(document, gurl));
1460 blink::WebURLRequest url_request = CreateWebURLRequest(document, gurl);
1462 ProgressEventRateLimiter* tracker = new ProgressEventRateLimiter(instance);
1464 // FileDownloader deletes itself after invoking DownloadNexeCompletion.
1465 FileDownloader* file_downloader = new FileDownloader(
1468 base::Bind(&DownloadFileCompletion, callback),
1469 base::Bind(&ProgressEventRateLimiter::ReportProgress,
1470 base::Owned(tracker), std::string(url)));
1471 file_downloader->Load(url_request);
1474 void ReportSelLdrStatus(PP_Instance instance,
1475 int32_t load_status,
1476 int32_t max_status) {
1477 HistogramEnumerate("NaCl.LoadStatus.SelLdr", load_status, max_status);
1478 NexeLoadManager* load_manager = GetNexeLoadManager(instance);
1479 DCHECK(load_manager);
1483 // Gather data to see if being installed changes load outcomes.
1484 const char* name = load_manager->is_installed() ?
1485 "NaCl.LoadStatus.SelLdr.InstalledApp" :
1486 "NaCl.LoadStatus.SelLdr.NotInstalledApp";
1487 HistogramEnumerate(name, load_status, max_status);
1490 void LogTranslateTime(const char* histogram_name,
1491 int64_t time_in_us) {
1492 ppapi::PpapiGlobals::Get()->GetMainThreadMessageLoop()->PostTask(
1494 base::Bind(&HistogramTimeTranslation,
1495 std::string(histogram_name),
1496 time_in_us / 1000));
1499 void DidOpenManifestEntry(PP_NaClFileInfo* out_file_info,
1500 PP_CompletionCallback callback,
1502 const PP_NaClFileInfo& file_info) {
1503 if (pp_error == PP_OK)
1504 *out_file_info = file_info;
1505 callback.func(callback.user_data, pp_error);
1508 void OpenManifestEntry(PP_Instance instance,
1509 PP_Bool is_helper_process,
1511 PP_NaClFileInfo* out_file_info,
1512 PP_CompletionCallback callback) {
1514 PP_PNaClOptions pnacl_options;
1515 pnacl_options.translate = PP_FALSE;
1516 pnacl_options.is_debug = PP_FALSE;
1517 pnacl_options.opt_level = 2;
1518 if (!ManifestResolveKey(instance,
1519 PP_ToBool(is_helper_process),
1523 PostPPCompletionCallback(callback, PP_ERROR_FAILED);
1526 // TODO(teravest): Make a type like PP_NaClFileInfo to use for DownloadFile
1527 // that would close the file handle on destruction.
1528 DownloadFile(instance, url,
1529 base::Bind(&DidOpenManifestEntry, out_file_info, callback));
1532 void SetPNaClStartTime(PP_Instance instance) {
1533 NexeLoadManager* load_manager = GetNexeLoadManager(instance);
1535 load_manager->set_pnacl_start_time(base::Time::Now());
1538 // PexeDownloader is responsible for deleting itself when the download
1540 class PexeDownloader : public blink::WebURLLoaderClient {
1542 PexeDownloader(PP_Instance instance,
1543 scoped_ptr<blink::WebURLLoader> url_loader,
1544 const std::string& pexe_url,
1545 int32_t pexe_opt_level,
1546 const PPP_PexeStreamHandler* stream_handler,
1547 void* stream_handler_user_data)
1548 : instance_(instance),
1549 url_loader_(url_loader.Pass()),
1550 pexe_url_(pexe_url),
1551 pexe_opt_level_(pexe_opt_level),
1552 stream_handler_(stream_handler),
1553 stream_handler_user_data_(stream_handler_user_data),
1555 expected_content_length_(-1),
1556 weak_factory_(this) { }
1558 void Load(const blink::WebURLRequest& request) {
1559 url_loader_->loadAsynchronously(request, this);
1563 virtual void didReceiveResponse(blink::WebURLLoader* loader,
1564 const blink::WebURLResponse& response) {
1565 success_ = (response.httpStatusCode() == 200);
1569 expected_content_length_ = response.expectedContentLength();
1571 // Defer loading after receiving headers. This is because we may already
1572 // have a cached translated nexe, so check for that now.
1573 url_loader_->setDefersLoading(true);
1575 std::string etag = response.httpHeaderField("etag").utf8();
1576 std::string last_modified =
1577 response.httpHeaderField("last-modified").utf8();
1578 base::Time last_modified_time;
1579 base::Time::FromString(last_modified.c_str(), &last_modified_time);
1581 bool has_no_store_header = false;
1582 std::string cache_control =
1583 response.httpHeaderField("cache-control").utf8();
1585 std::vector<std::string> values;
1586 base::SplitString(cache_control, ',', &values);
1587 for (std::vector<std::string>::const_iterator it = values.begin();
1590 if (base::StringToLowerASCII(*it) == "no-store")
1591 has_no_store_header = true;
1594 GetNexeFd(instance_,
1599 has_no_store_header,
1600 base::Bind(&PexeDownloader::didGetNexeFd,
1601 weak_factory_.GetWeakPtr()));
1604 virtual void didGetNexeFd(int32_t pp_error,
1606 PP_FileHandle file_handle) {
1607 if (!content::PepperPluginInstance::Get(instance_)) {
1612 HistogramEnumerate("NaCl.Perf.PNaClCache.IsHit", cache_hit, 2);
1614 stream_handler_->DidCacheHit(stream_handler_user_data_, file_handle);
1616 // We delete the PexeDownloader at this point since we successfully got a
1617 // cached, translated nexe.
1621 stream_handler_->DidCacheMiss(stream_handler_user_data_,
1622 expected_content_length_,
1625 // No translated nexe was found in the cache, so we should download the
1626 // file to start streaming it.
1627 url_loader_->setDefersLoading(false);
1630 virtual void didReceiveData(blink::WebURLLoader* loader,
1633 int encoded_data_length) {
1634 if (content::PepperPluginInstance::Get(instance_)) {
1635 // Stream the data we received to the stream callback.
1636 stream_handler_->DidStreamData(stream_handler_user_data_,
1642 virtual void didFinishLoading(blink::WebURLLoader* loader,
1644 int64_t total_encoded_data_length) {
1645 int32_t result = success_ ? PP_OK : PP_ERROR_FAILED;
1647 if (content::PepperPluginInstance::Get(instance_))
1648 stream_handler_->DidFinishStream(stream_handler_user_data_, result);
1652 virtual void didFail(blink::WebURLLoader* loader,
1653 const blink::WebURLError& error) {
1657 PP_Instance instance_;
1658 scoped_ptr<blink::WebURLLoader> url_loader_;
1659 std::string pexe_url_;
1660 int32_t pexe_opt_level_;
1661 const PPP_PexeStreamHandler* stream_handler_;
1662 void* stream_handler_user_data_;
1664 int64_t expected_content_length_;
1665 base::WeakPtrFactory<PexeDownloader> weak_factory_;
1668 void StreamPexe(PP_Instance instance,
1669 const char* pexe_url,
1671 const PPP_PexeStreamHandler* handler,
1672 void* handler_user_data) {
1673 content::PepperPluginInstance* plugin_instance =
1674 content::PepperPluginInstance::Get(instance);
1675 if (!plugin_instance) {
1676 base::MessageLoop::current()->PostTask(
1678 base::Bind(handler->DidFinishStream,
1680 static_cast<int32_t>(PP_ERROR_FAILED)));
1684 GURL gurl(pexe_url);
1685 const blink::WebDocument& document =
1686 plugin_instance->GetContainer()->element().document();
1687 scoped_ptr<blink::WebURLLoader> url_loader(
1688 CreateWebURLLoader(document, gurl));
1689 PexeDownloader* downloader = new PexeDownloader(instance,
1696 blink::WebURLRequest url_request = CreateWebURLRequest(document, gurl);
1697 // Mark the request as requesting a PNaCl bitcode file,
1698 // so that component updater can detect this user action.
1699 url_request.addHTTPHeaderField(
1700 blink::WebString::fromUTF8("Accept"),
1701 blink::WebString::fromUTF8("application/x-pnacl, */*"));
1702 url_request.setRequestContext(blink::WebURLRequest::RequestContextObject);
1703 downloader->Load(url_request);
1706 const PPB_NaCl_Private nacl_interface = {
1710 &Are3DInterfacesDisabled,
1711 &BrokerDuplicateHandle,
1712 &GetReadExecPnaclFd,
1713 &CreateTemporaryFile,
1714 &GetNumberOfProcessors,
1715 &PPIsNonSFIModeEnabled,
1716 &ReportTranslationFinished,
1724 &NaClDebugEnabledForURL,
1733 &RequestNaClManifest,
1734 &GetManifestBaseURL,
1735 &ProcessNaClManifest,
1736 &DevInterfacesEnabled,
1737 &ManifestGetProgramURL,
1738 &GetPNaClResourceInfo,
1739 &GetCpuFeatureAttrs,
1740 &PostMessageToJavaScript,
1742 &ReportSelLdrStatus,
1751 const PPB_NaCl_Private* GetNaClPrivateInterface() {
1752 return &nacl_interface;