Revert 285028 "Pepper: Remove LOAD_MODULE SRPC call in SFI mode."
[chromium-blink-merge.git] / components / nacl / renderer / ppb_nacl_private_impl.cc
blob9ada43de3774f1284e3f421254621d9c99c50297
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"
7 #include <numeric>
8 #include <string>
9 #include <vector>
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"
15 #include "base/cpu.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"
67 namespace nacl {
68 namespace {
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();
82 if (!render_thread)
83 return false;
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());
89 return true;
92 struct InstanceInfo {
93 InstanceInfo() : plugin_pid(base::kNullProcessId), plugin_child_id(0) {}
94 GURL url;
95 ppapi::PpapiPermissions permissions;
96 base::ProcessId plugin_pid;
97 int plugin_child_id;
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>
107 NexeLoadManagerMap;
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())
116 return iter->second;
117 return NULL;
120 static const PP_NaClFileInfo kInvalidNaClFileInfo = {
121 PP_kInvalidFileHandle,
122 0, // token_lo
123 0, // token_hi
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);
131 if (!host)
132 return 0;
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()) {
139 return false;
142 #if defined(OS_POSIX)
143 if (channel_handle.socket.fd == -1) {
144 return false;
146 #endif
148 return true;
151 void PostPPCompletionCallback(PP_CompletionCallback callback,
152 int32_t status) {
153 ppapi::PpapiGlobals::Get()->GetMainThreadMessageLoop()->PostTask(
154 FROM_HERE,
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
176 // management.
177 class ManifestServiceProxy : public ManifestServiceChannel::Delegate {
178 public:
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,
195 &pnacl_options,
196 &uses_nonsfi_mode,
197 &error_info)) {
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());
211 std::string url;
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(
220 FROM_HERE,
221 base::Bind(callback, base::Passed(base::File())));
222 return;
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
228 // object either.
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));
236 private:
237 static void DidDownloadFile(
238 ManifestServiceChannel::OpenResourceCallback callback,
239 int32_t pp_error,
240 const PP_NaClFileInfo& file_info) {
241 if (pp_error != PP_OK) {
242 callback.Run(base::File());
243 return;
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,
253 const GURL& gurl) {
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;
261 } else {
262 // Allow CORS.
263 options.crossOriginRequestPolicy =
264 blink::WebURLLoaderOptions::CrossOriginRequestPolicyUseAccessControl;
266 return document.frame()->createAssociatedURLLoader(options);
269 blink::WebURLRequest CreateWebURLRequest(const blink::WebDocument& document,
270 const GURL& gurl) {
271 blink::WebURLRequest request;
272 request.initialize();
273 request.setURL(gurl);
274 request.setFirstPartyForCookies(document.firstPartyForCookies());
275 return request;
278 int32_t FileDownloaderToPepperError(FileDownloader::Status status) {
279 switch (status) {
280 case FileDownloader::SUCCESS:
281 return PP_OK;
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,
296 PP_Bool uses_irt,
297 PP_Bool uses_ppapi,
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,
303 void* imc_handle,
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();
315 DCHECK(sender);
316 int routing_id = 0;
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.
321 if (uses_ppapi) {
322 routing_id = GetRoutingID(instance);
323 if (!routing_id) {
324 if (nexe_file_info->handle != PP_kInvalidFileHandle) {
325 base::File closer(nexe_file_info->handle);
327 ppapi::PpapiGlobals::Get()->GetMainThreadMessageLoop()->PostTask(
328 FROM_HERE,
329 base::Bind(callback.func, callback.user_data,
330 static_cast<int32_t>(PP_ERROR_FAILED)));
331 return;
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 content::RendererPpapiHost* host =
350 content::RendererPpapiHost::GetForPPInstance(instance);
351 if (!sender->Send(new NaClHostMsg_LaunchNaCl(
352 NaClLaunchParams(
353 instance_info.url.spec(),
354 host->ShareHandleWithRemote(nexe_file_info->handle, true),
355 routing_id,
356 perm_bits,
357 PP_ToBool(uses_irt),
358 PP_ToBool(uses_nonsfi_mode),
359 PP_ToBool(enable_dyncode_syscalls),
360 PP_ToBool(enable_exception_handling),
361 PP_ToBool(enable_crash_throttling)),
362 &launch_result,
363 &error_message_string))) {
364 ppapi::PpapiGlobals::Get()->GetMainThreadMessageLoop()->PostTask(
365 FROM_HERE,
366 base::Bind(callback.func, callback.user_data,
367 static_cast<int32_t>(PP_ERROR_FAILED)));
368 return;
371 if (!error_message_string.empty()) {
372 if (PP_ToBool(main_service_runtime)) {
373 NexeLoadManager* load_manager = GetNexeLoadManager(instance);
374 if (load_manager) {
375 load_manager->ReportLoadError(PP_NACL_ERROR_SEL_LDR_LAUNCH,
376 "ServiceRuntime: failed to start",
377 error_message_string);
380 ppapi::PpapiGlobals::Get()->GetMainThreadMessageLoop()->PostTask(
381 FROM_HERE,
382 base::Bind(callback.func, callback.user_data,
383 static_cast<int32_t>(PP_ERROR_FAILED)));
384 return;
386 result_socket = launch_result.imc_channel_handle;
387 instance_info.channel_handle = launch_result.ppapi_ipc_channel_handle;
388 instance_info.plugin_pid = launch_result.plugin_pid;
389 instance_info.plugin_child_id = launch_result.plugin_child_id;
391 // Don't save instance_info if channel handle is invalid.
392 if (IsValidChannelHandle(instance_info.channel_handle))
393 g_instance_info.Get()[instance] = instance_info;
395 *(static_cast<NaClHandle*>(imc_handle)) = ToNativeHandle(result_socket);
397 NexeLoadManager* load_manager = GetNexeLoadManager(instance);
398 DCHECK(load_manager);
399 if (!load_manager) {
400 PostPPCompletionCallback(callback, PP_ERROR_FAILED);
401 return;
404 // Create the trusted plugin channel.
405 if (IsValidChannelHandle(launch_result.trusted_ipc_channel_handle)) {
406 scoped_ptr<TrustedPluginChannel> trusted_plugin_channel(
407 new TrustedPluginChannel(
408 launch_result.trusted_ipc_channel_handle));
409 load_manager->set_trusted_plugin_channel(trusted_plugin_channel.Pass());
410 } else {
411 PostPPCompletionCallback(callback, PP_ERROR_FAILED);
412 return;
415 // Create the manifest service handle as well.
416 // For security hardening, disable the IPCs for open_resource() when they
417 // aren't needed. PNaCl doesn't expose open_resource(), and the new
418 // open_resource() IPCs are currently only used for Non-SFI NaCl so far,
419 // not SFI NaCl. Note that enable_dyncode_syscalls is true if and only if
420 // the plugin is a non-PNaCl plugin.
421 if (load_manager &&
422 enable_dyncode_syscalls &&
423 uses_nonsfi_mode &&
424 IsValidChannelHandle(
425 launch_result.manifest_service_ipc_channel_handle)) {
426 scoped_ptr<ManifestServiceChannel> manifest_service_channel(
427 new ManifestServiceChannel(
428 launch_result.manifest_service_ipc_channel_handle,
429 base::Bind(&PostPPCompletionCallback, callback),
430 manifest_service_proxy.Pass(),
431 content::RenderThread::Get()->GetShutdownEvent()));
432 load_manager->set_manifest_service_channel(
433 manifest_service_channel.Pass());
434 } else {
435 // Currently, manifest service works only on linux/non-SFI mode.
436 // On other platforms, the socket will not be created, and thus this
437 // condition needs to be handled as success.
438 PostPPCompletionCallback(callback, PP_OK);
442 PP_Bool StartPpapiProxy(PP_Instance instance) {
443 NexeLoadManager* load_manager = GetNexeLoadManager(instance);
444 DCHECK(load_manager);
445 if (!load_manager)
446 return PP_FALSE;
448 content::PepperPluginInstance* plugin_instance =
449 content::PepperPluginInstance::Get(instance);
450 if (!plugin_instance) {
451 DLOG(ERROR) << "GetInstance() failed";
452 return PP_FALSE;
455 InstanceInfoMap& map = g_instance_info.Get();
456 InstanceInfoMap::iterator it = map.find(instance);
457 if (it == map.end()) {
458 DLOG(ERROR) << "Could not find instance ID";
459 return PP_FALSE;
461 InstanceInfo instance_info = it->second;
462 map.erase(it);
464 PP_ExternalPluginResult result = plugin_instance->SwitchToOutOfProcessProxy(
465 base::FilePath().AppendASCII(instance_info.url.spec()),
466 instance_info.permissions,
467 instance_info.channel_handle,
468 instance_info.plugin_pid,
469 instance_info.plugin_child_id);
471 if (result == PP_EXTERNAL_PLUGIN_OK) {
472 // Log the amound of time that has passed between the trusted plugin being
473 // initialized and the untrusted plugin being initialized. This is
474 // (roughly) the cost of using NaCl, in terms of startup time.
475 load_manager->ReportStartupOverhead();
476 return PP_TRUE;
477 } else if (result == PP_EXTERNAL_PLUGIN_ERROR_MODULE) {
478 load_manager->ReportLoadError(PP_NACL_ERROR_START_PROXY_MODULE,
479 "could not initialize module.");
480 } else if (result == PP_EXTERNAL_PLUGIN_ERROR_INSTANCE) {
481 load_manager->ReportLoadError(PP_NACL_ERROR_START_PROXY_MODULE,
482 "could not create instance.");
484 return PP_FALSE;
487 int UrandomFD(void) {
488 #if defined(OS_POSIX)
489 return base::GetUrandomFD();
490 #else
491 return -1;
492 #endif
495 PP_Bool Are3DInterfacesDisabled() {
496 return PP_FromBool(CommandLine::ForCurrentProcess()->HasSwitch(
497 switches::kDisable3DAPIs));
500 int32_t BrokerDuplicateHandle(PP_FileHandle source_handle,
501 uint32_t process_id,
502 PP_FileHandle* target_handle,
503 uint32_t desired_access,
504 uint32_t options) {
505 #if defined(OS_WIN)
506 return content::BrokerDuplicateHandle(source_handle, process_id,
507 target_handle, desired_access,
508 options);
509 #else
510 return 0;
511 #endif
514 // Convert a URL to a filename for GetReadonlyPnaclFd.
515 // Must be kept in sync with PnaclCanOpenFile() in
516 // components/nacl/browser/nacl_file_host.cc.
517 std::string PnaclComponentURLToFilename(const std::string& url) {
518 // PNaCl component URLs aren't arbitrary URLs; they are always either
519 // generated from ManifestResolveKey or PnaclResources::ReadResourceInfo.
520 // So, it's safe to just use string parsing operations here instead of
521 // URL-parsing ones.
522 DCHECK(StartsWithASCII(url, kPNaClTranslatorBaseUrl, true));
523 std::string r = url.substr(std::string(kPNaClTranslatorBaseUrl).length());
525 // Use white-listed-chars.
526 size_t replace_pos;
527 static const char* white_list = "abcdefghijklmnopqrstuvwxyz0123456789_";
528 replace_pos = r.find_first_not_of(white_list);
529 while(replace_pos != std::string::npos) {
530 r = r.replace(replace_pos, 1, "_");
531 replace_pos = r.find_first_not_of(white_list);
533 return r;
536 PP_FileHandle GetReadonlyPnaclFd(const char* url,
537 bool is_executable,
538 uint64_t* nonce_lo,
539 uint64_t* nonce_hi) {
540 std::string filename = PnaclComponentURLToFilename(url);
541 IPC::PlatformFileForTransit out_fd = IPC::InvalidPlatformFileForTransit();
542 IPC::Sender* sender = content::RenderThread::Get();
543 DCHECK(sender);
544 if (!sender->Send(new NaClHostMsg_GetReadonlyPnaclFD(
545 std::string(filename), is_executable,
546 &out_fd, nonce_lo, nonce_hi))) {
547 return PP_kInvalidFileHandle;
549 if (out_fd == IPC::InvalidPlatformFileForTransit()) {
550 return PP_kInvalidFileHandle;
552 return IPC::PlatformFileForTransitToPlatformFile(out_fd);
555 void GetReadExecPnaclFd(const char* url,
556 PP_NaClFileInfo* out_file_info) {
557 *out_file_info = kInvalidNaClFileInfo;
558 out_file_info->handle = GetReadonlyPnaclFd(url, true /* is_executable */,
559 &out_file_info->token_lo,
560 &out_file_info->token_hi);
563 PP_FileHandle CreateTemporaryFile(PP_Instance instance) {
564 IPC::PlatformFileForTransit transit_fd = IPC::InvalidPlatformFileForTransit();
565 IPC::Sender* sender = content::RenderThread::Get();
566 DCHECK(sender);
567 if (!sender->Send(new NaClHostMsg_NaClCreateTemporaryFile(
568 &transit_fd))) {
569 return PP_kInvalidFileHandle;
572 if (transit_fd == IPC::InvalidPlatformFileForTransit()) {
573 return PP_kInvalidFileHandle;
576 return IPC::PlatformFileForTransitToPlatformFile(transit_fd);
579 int32_t GetNumberOfProcessors() {
580 int32_t num_processors;
581 IPC::Sender* sender = content::RenderThread::Get();
582 DCHECK(sender);
583 if(!sender->Send(new NaClHostMsg_NaClGetNumProcessors(&num_processors))) {
584 return 1;
586 return num_processors;
589 PP_Bool PPIsNonSFIModeEnabled() {
590 return PP_FromBool(IsNonSFIModeEnabled());
593 void GetNexeFd(PP_Instance instance,
594 const std::string& pexe_url,
595 uint32_t opt_level,
596 const base::Time& last_modified_time,
597 const std::string& etag,
598 bool has_no_store_header,
599 base::Callback<void(int32_t, bool, PP_FileHandle)> callback) {
600 if (!InitializePnaclResourceHost()) {
601 ppapi::PpapiGlobals::Get()->GetMainThreadMessageLoop()->PostTask(
602 FROM_HERE,
603 base::Bind(callback,
604 static_cast<int32_t>(PP_ERROR_FAILED),
605 false,
606 PP_kInvalidFileHandle));
607 return;
610 PnaclCacheInfo cache_info;
611 cache_info.pexe_url = GURL(pexe_url);
612 // TODO(dschuff): Get this value from the pnacl json file after it
613 // rolls in from NaCl.
614 cache_info.abi_version = 1;
615 cache_info.opt_level = opt_level;
616 cache_info.last_modified = last_modified_time;
617 cache_info.etag = etag;
618 cache_info.has_no_store_header = has_no_store_header;
619 cache_info.sandbox_isa = GetSandboxArch();
620 cache_info.extra_flags = GetCpuFeatures();
622 g_pnacl_resource_host.Get()->RequestNexeFd(
623 GetRoutingID(instance),
624 instance,
625 cache_info,
626 callback);
629 void ReportTranslationFinished(PP_Instance instance,
630 PP_Bool success,
631 int32_t opt_level,
632 int64_t pexe_size,
633 int64_t compile_time_us) {
634 if (success == PP_TRUE) {
635 static const int32_t kUnknownOptLevel = 4;
636 if (opt_level < 0 || opt_level > 3)
637 opt_level = kUnknownOptLevel;
638 HistogramEnumerate("NaCl.Options.PNaCl.OptLevel",
639 opt_level,
640 kUnknownOptLevel + 1);
641 HistogramKBPerSec("NaCl.Perf.PNaClLoadTime.CompileKBPerSec",
642 pexe_size / 1024,
643 compile_time_us);
644 HistogramSizeKB("NaCl.Perf.Size.Pexe", pexe_size / 1024);
646 NexeLoadManager* load_manager = GetNexeLoadManager(instance);
647 if (load_manager) {
648 base::TimeDelta total_time = base::Time::Now() -
649 load_manager->pnacl_start_time();
650 HistogramTimeTranslation("NaCl.Perf.PNaClLoadTime.TotalUncachedTime",
651 total_time.InMilliseconds());
652 HistogramKBPerSec("NaCl.Perf.PNaClLoadTime.TotalUncachedKBPerSec",
653 pexe_size / 1024,
654 total_time.InMicroseconds());
658 // If the resource host isn't initialized, don't try to do that here.
659 // Just return because something is already very wrong.
660 if (g_pnacl_resource_host.Get() == NULL)
661 return;
662 g_pnacl_resource_host.Get()->ReportTranslationFinished(instance, success);
665 PP_FileHandle OpenNaClExecutable(PP_Instance instance,
666 const char* file_url,
667 uint64_t* nonce_lo,
668 uint64_t* nonce_hi) {
669 // Fast path only works for installed file URLs.
670 GURL gurl(file_url);
671 if (!gurl.SchemeIs("chrome-extension"))
672 return PP_kInvalidFileHandle;
674 content::PepperPluginInstance* plugin_instance =
675 content::PepperPluginInstance::Get(instance);
676 if (!plugin_instance)
677 return PP_kInvalidFileHandle;
678 // IMPORTANT: Make sure the document can request the given URL. If we don't
679 // check, a malicious app could probe the extension system. This enforces a
680 // same-origin policy which prevents the app from requesting resources from
681 // another app.
682 blink::WebSecurityOrigin security_origin =
683 plugin_instance->GetContainer()->element().document().securityOrigin();
684 if (!security_origin.canRequest(gurl))
685 return PP_kInvalidFileHandle;
687 IPC::PlatformFileForTransit out_fd = IPC::InvalidPlatformFileForTransit();
688 IPC::Sender* sender = content::RenderThread::Get();
689 DCHECK(sender);
690 *nonce_lo = 0;
691 *nonce_hi = 0;
692 base::FilePath file_path;
693 if (!sender->Send(
694 new NaClHostMsg_OpenNaClExecutable(GetRoutingID(instance),
695 GURL(file_url),
696 &out_fd,
697 nonce_lo,
698 nonce_hi))) {
699 return PP_kInvalidFileHandle;
702 if (out_fd == IPC::InvalidPlatformFileForTransit())
703 return PP_kInvalidFileHandle;
705 return IPC::PlatformFileForTransitToPlatformFile(out_fd);
708 void DispatchEvent(PP_Instance instance,
709 PP_NaClEventType event_type,
710 const char *resource_url,
711 PP_Bool length_is_computable,
712 uint64_t loaded_bytes,
713 uint64_t total_bytes) {
714 ProgressEvent event(event_type,
715 resource_url,
716 PP_ToBool(length_is_computable),
717 loaded_bytes,
718 total_bytes);
719 DispatchProgressEvent(instance, event);
722 void ReportLoadSuccess(PP_Instance instance,
723 uint64_t loaded_bytes,
724 uint64_t total_bytes) {
725 NexeLoadManager* load_manager = GetNexeLoadManager(instance);
726 if (load_manager) {
727 load_manager->ReportLoadSuccess(load_manager->program_url(),
728 loaded_bytes,
729 total_bytes);
733 void ReportLoadError(PP_Instance instance,
734 PP_NaClError error,
735 const char* error_message) {
736 NexeLoadManager* load_manager = GetNexeLoadManager(instance);
737 if (load_manager)
738 load_manager->ReportLoadError(error, error_message);
741 void ReportLoadAbort(PP_Instance instance) {
742 NexeLoadManager* load_manager = GetNexeLoadManager(instance);
743 if (load_manager)
744 load_manager->ReportLoadAbort();
747 void NexeDidCrash(PP_Instance instance, const char* crash_log) {
748 NexeLoadManager* load_manager = GetNexeLoadManager(instance);
749 if (load_manager)
750 load_manager->NexeDidCrash(crash_log);
753 void InstanceCreated(PP_Instance instance) {
754 scoped_ptr<NexeLoadManager> new_load_manager(new NexeLoadManager(instance));
755 NexeLoadManagerMap& map = g_load_manager_map.Get();
756 DLOG_IF(ERROR, map.count(instance) != 0) << "Instance count should be 0";
757 map.add(instance, new_load_manager.Pass());
760 void InstanceDestroyed(PP_Instance instance) {
761 DeleteJsonManifest(instance);
763 NexeLoadManagerMap& map = g_load_manager_map.Get();
764 DLOG_IF(ERROR, map.count(instance) == 0) << "Could not find instance ID";
765 // The erase may call NexeLoadManager's destructor prior to removing it from
766 // the map. In that case, it is possible for the trusted Plugin to re-enter
767 // the NexeLoadManager (e.g., by calling ReportLoadError). Passing out the
768 // NexeLoadManager to a local scoped_ptr just ensures that its entry is gone
769 // from the map prior to the destructor being invoked.
770 scoped_ptr<NexeLoadManager> temp(map.take(instance));
771 map.erase(instance);
774 PP_Bool NaClDebugEnabledForURL(const char* alleged_nmf_url) {
775 if (!CommandLine::ForCurrentProcess()->HasSwitch(switches::kEnableNaClDebug))
776 return PP_FALSE;
777 bool should_debug;
778 IPC::Sender* sender = content::RenderThread::Get();
779 DCHECK(sender);
780 if(!sender->Send(new NaClHostMsg_NaClDebugEnabledForURL(
781 GURL(alleged_nmf_url),
782 &should_debug))) {
783 return PP_FALSE;
785 return PP_FromBool(should_debug);
788 void LogToConsole(PP_Instance instance, const char* message) {
789 NexeLoadManager* load_manager = GetNexeLoadManager(instance);
790 DCHECK(load_manager);
791 if (load_manager)
792 load_manager->LogToConsole(std::string(message));
795 PP_NaClReadyState GetNaClReadyState(PP_Instance instance) {
796 NexeLoadManager* load_manager = GetNexeLoadManager(instance);
797 DCHECK(load_manager);
798 if (load_manager)
799 return load_manager->nacl_ready_state();
800 return PP_NACL_READY_STATE_UNSENT;
803 int32_t GetExitStatus(PP_Instance instance) {
804 NexeLoadManager* load_manager = GetNexeLoadManager(instance);
805 DCHECK(load_manager);
806 if (load_manager)
807 return load_manager->exit_status();
808 return -1;
811 void SetExitStatus(PP_Instance instance, int32_t exit_status) {
812 NexeLoadManager* load_manager = GetNexeLoadManager(instance);
813 DCHECK(load_manager);
814 if (load_manager)
815 return load_manager->set_exit_status(exit_status);
818 void Vlog(const char* message) {
819 VLOG(1) << message;
822 void InitializePlugin(PP_Instance instance,
823 uint32_t argc,
824 const char* argn[],
825 const char* argv[]) {
826 NexeLoadManager* load_manager = GetNexeLoadManager(instance);
827 DCHECK(load_manager);
828 if (load_manager)
829 load_manager->InitializePlugin(argc, argn, argv);
832 int64_t GetNexeSize(PP_Instance instance) {
833 NexeLoadManager* load_manager = GetNexeLoadManager(instance);
834 DCHECK(load_manager);
835 if (load_manager)
836 return load_manager->nexe_size();
837 return 0;
840 void DownloadManifestToBuffer(PP_Instance instance,
841 struct PP_CompletionCallback callback);
843 bool CreateJsonManifest(PP_Instance instance,
844 const std::string& manifest_url,
845 const std::string& manifest_data);
847 void RequestNaClManifest(PP_Instance instance,
848 PP_CompletionCallback callback) {
849 NexeLoadManager* load_manager = GetNexeLoadManager(instance);
850 DCHECK(load_manager);
851 if (!load_manager) {
852 ppapi::PpapiGlobals::Get()->GetMainThreadMessageLoop()->PostTask(
853 FROM_HERE,
854 base::Bind(callback.func, callback.user_data,
855 static_cast<int32_t>(PP_ERROR_FAILED)));
856 return;
859 std::string url = load_manager->GetManifestURLArgument();
860 if (url.empty() || !load_manager->RequestNaClManifest(url)) {
861 ppapi::PpapiGlobals::Get()->GetMainThreadMessageLoop()->PostTask(
862 FROM_HERE,
863 base::Bind(callback.func, callback.user_data,
864 static_cast<int32_t>(PP_ERROR_FAILED)));
865 return;
868 const GURL& base_url = load_manager->manifest_base_url();
869 if (base_url.SchemeIs("data")) {
870 GURL gurl(base_url);
871 std::string mime_type;
872 std::string charset;
873 std::string data;
874 int32_t error = PP_ERROR_FAILED;
875 if (net::DataURL::Parse(gurl, &mime_type, &charset, &data)) {
876 if (data.size() <= ManifestDownloader::kNaClManifestMaxFileBytes) {
877 if (CreateJsonManifest(instance, base_url.spec(), data))
878 error = PP_OK;
879 } else {
880 load_manager->ReportLoadError(PP_NACL_ERROR_MANIFEST_TOO_LARGE,
881 "manifest file too large.");
883 } else {
884 load_manager->ReportLoadError(PP_NACL_ERROR_MANIFEST_LOAD_URL,
885 "could not load manifest url.");
887 ppapi::PpapiGlobals::Get()->GetMainThreadMessageLoop()->PostTask(
888 FROM_HERE,
889 base::Bind(callback.func, callback.user_data, error));
890 } else {
891 DownloadManifestToBuffer(instance, callback);
895 PP_Var GetManifestBaseURL(PP_Instance instance) {
896 NexeLoadManager* load_manager = GetNexeLoadManager(instance);
897 DCHECK(load_manager);
898 if (!load_manager)
899 return PP_MakeUndefined();
900 const GURL& gurl = load_manager->manifest_base_url();
901 if (!gurl.is_valid())
902 return PP_MakeUndefined();
903 return ppapi::StringVar::StringToPPVar(gurl.spec());
906 void ProcessNaClManifest(PP_Instance instance, const char* program_url) {
907 nacl::NexeLoadManager* load_manager = GetNexeLoadManager(instance);
908 if (load_manager)
909 load_manager->ProcessNaClManifest(program_url);
912 PP_Bool DevInterfacesEnabled(PP_Instance instance) {
913 nacl::NexeLoadManager* load_manager = GetNexeLoadManager(instance);
914 if (load_manager)
915 return PP_FromBool(load_manager->DevInterfacesEnabled());
916 return PP_FALSE;
919 void DownloadManifestToBufferCompletion(PP_Instance instance,
920 struct PP_CompletionCallback callback,
921 base::Time start_time,
922 PP_NaClError pp_nacl_error,
923 const std::string& data);
925 void DownloadManifestToBuffer(PP_Instance instance,
926 struct PP_CompletionCallback callback) {
927 nacl::NexeLoadManager* load_manager = GetNexeLoadManager(instance);
928 DCHECK(load_manager);
929 content::PepperPluginInstance* plugin_instance =
930 content::PepperPluginInstance::Get(instance);
931 if (!load_manager || !plugin_instance) {
932 ppapi::PpapiGlobals::Get()->GetMainThreadMessageLoop()->PostTask(
933 FROM_HERE,
934 base::Bind(callback.func, callback.user_data,
935 static_cast<int32_t>(PP_ERROR_FAILED)));
937 const blink::WebDocument& document =
938 plugin_instance->GetContainer()->element().document();
940 const GURL& gurl = load_manager->manifest_base_url();
941 scoped_ptr<blink::WebURLLoader> url_loader(
942 CreateWebURLLoader(document, gurl));
943 blink::WebURLRequest request = CreateWebURLRequest(document, gurl);
945 // ManifestDownloader deletes itself after invoking the callback.
946 ManifestDownloader* manifest_downloader = new ManifestDownloader(
947 url_loader.Pass(),
948 load_manager->is_installed(),
949 base::Bind(DownloadManifestToBufferCompletion,
950 instance, callback, base::Time::Now()));
951 manifest_downloader->Load(request);
954 void DownloadManifestToBufferCompletion(PP_Instance instance,
955 struct PP_CompletionCallback callback,
956 base::Time start_time,
957 PP_NaClError pp_nacl_error,
958 const std::string& data) {
959 base::TimeDelta download_time = base::Time::Now() - start_time;
960 HistogramTimeSmall("NaCl.Perf.StartupTime.ManifestDownload",
961 download_time.InMilliseconds());
963 nacl::NexeLoadManager* load_manager = GetNexeLoadManager(instance);
964 if (!load_manager) {
965 callback.func(callback.user_data, PP_ERROR_ABORTED);
966 return;
969 int32_t pp_error;
970 switch (pp_nacl_error) {
971 case PP_NACL_ERROR_LOAD_SUCCESS:
972 pp_error = PP_OK;
973 break;
974 case PP_NACL_ERROR_MANIFEST_LOAD_URL:
975 pp_error = PP_ERROR_FAILED;
976 load_manager->ReportLoadError(PP_NACL_ERROR_MANIFEST_LOAD_URL,
977 "could not load manifest url.");
978 break;
979 case PP_NACL_ERROR_MANIFEST_TOO_LARGE:
980 pp_error = PP_ERROR_FILETOOBIG;
981 load_manager->ReportLoadError(PP_NACL_ERROR_MANIFEST_TOO_LARGE,
982 "manifest file too large.");
983 break;
984 case PP_NACL_ERROR_MANIFEST_NOACCESS_URL:
985 pp_error = PP_ERROR_NOACCESS;
986 load_manager->ReportLoadError(PP_NACL_ERROR_MANIFEST_NOACCESS_URL,
987 "access to manifest url was denied.");
988 break;
989 default:
990 NOTREACHED();
991 pp_error = PP_ERROR_FAILED;
992 load_manager->ReportLoadError(PP_NACL_ERROR_MANIFEST_LOAD_URL,
993 "could not load manifest url.");
996 if (pp_error == PP_OK) {
997 std::string base_url = load_manager->manifest_base_url().spec();
998 if (!CreateJsonManifest(instance, base_url, data))
999 pp_error = PP_ERROR_FAILED;
1001 callback.func(callback.user_data, pp_error);
1004 bool CreateJsonManifest(PP_Instance instance,
1005 const std::string& manifest_url,
1006 const std::string& manifest_data) {
1007 HistogramSizeKB("NaCl.Perf.Size.Manifest",
1008 static_cast<int32_t>(manifest_data.length() / 1024));
1010 nacl::NexeLoadManager* load_manager = GetNexeLoadManager(instance);
1011 if (!load_manager)
1012 return false;
1014 const char* isa_type;
1015 if (load_manager->IsPNaCl())
1016 isa_type = kPortableArch;
1017 else
1018 isa_type = GetSandboxArch();
1020 scoped_ptr<nacl::JsonManifest> j(
1021 new nacl::JsonManifest(
1022 manifest_url.c_str(),
1023 isa_type,
1024 IsNonSFIModeEnabled(),
1025 PP_ToBool(NaClDebugEnabledForURL(manifest_url.c_str()))));
1026 JsonManifest::ErrorInfo error_info;
1027 if (j->Init(manifest_data.c_str(), &error_info)) {
1028 AddJsonManifest(instance, j.Pass());
1029 return true;
1031 load_manager->ReportLoadError(error_info.error, error_info.string);
1032 return false;
1035 PP_Bool ManifestGetProgramURL(PP_Instance instance,
1036 PP_Var* pp_full_url,
1037 PP_PNaClOptions* pnacl_options,
1038 PP_Bool* pp_uses_nonsfi_mode) {
1039 nacl::NexeLoadManager* load_manager = GetNexeLoadManager(instance);
1041 JsonManifest* manifest = GetJsonManifest(instance);
1042 if (manifest == NULL)
1043 return PP_FALSE;
1045 bool uses_nonsfi_mode;
1046 std::string full_url;
1047 JsonManifest::ErrorInfo error_info;
1048 if (manifest->GetProgramURL(&full_url, pnacl_options, &uses_nonsfi_mode,
1049 &error_info)) {
1050 *pp_full_url = ppapi::StringVar::StringToPPVar(full_url);
1051 *pp_uses_nonsfi_mode = PP_FromBool(uses_nonsfi_mode);
1052 return PP_TRUE;
1055 if (load_manager)
1056 load_manager->ReportLoadError(error_info.error, error_info.string);
1057 return PP_FALSE;
1060 bool ManifestResolveKey(PP_Instance instance,
1061 bool is_helper_process,
1062 const std::string& key,
1063 std::string* full_url,
1064 PP_PNaClOptions* pnacl_options) {
1065 // For "helper" processes (llc and ld, for PNaCl translation), we resolve
1066 // keys manually as there is no existing .nmf file to parse.
1067 if (is_helper_process) {
1068 pnacl_options->translate = PP_FALSE;
1069 // We can only resolve keys in the files/ namespace.
1070 const std::string kFilesPrefix = "files/";
1071 if (key.find(kFilesPrefix) == std::string::npos) {
1072 nacl::NexeLoadManager* load_manager = GetNexeLoadManager(instance);
1073 if (load_manager)
1074 load_manager->ReportLoadError(PP_NACL_ERROR_MANIFEST_RESOLVE_URL,
1075 "key did not start with files/");
1076 return false;
1078 std::string key_basename = key.substr(kFilesPrefix.length());
1079 *full_url = std::string(kPNaClTranslatorBaseUrl) + GetSandboxArch() + "/" +
1080 key_basename;
1081 return true;
1084 JsonManifest* manifest = GetJsonManifest(instance);
1085 if (manifest == NULL)
1086 return false;
1088 return manifest->ResolveKey(key, full_url, pnacl_options);
1091 PP_Bool GetPNaClResourceInfo(PP_Instance instance,
1092 PP_Var* llc_tool_name,
1093 PP_Var* ld_tool_name) {
1094 static const char kFilename[] = "chrome://pnacl-translator/pnacl.json";
1095 NexeLoadManager* load_manager = GetNexeLoadManager(instance);
1096 DCHECK(load_manager);
1097 if (!load_manager)
1098 return PP_FALSE;
1100 uint64_t nonce_lo = 0;
1101 uint64_t nonce_hi = 0;
1102 base::File file(GetReadonlyPnaclFd(kFilename, false /* is_executable */,
1103 &nonce_lo, &nonce_hi));
1104 if (!file.IsValid()) {
1105 load_manager->ReportLoadError(
1106 PP_NACL_ERROR_PNACL_RESOURCE_FETCH,
1107 "The Portable Native Client (pnacl) component is not "
1108 "installed. Please consult chrome://components for more "
1109 "information.");
1110 return PP_FALSE;
1113 base::File::Info file_info;
1114 if (!file.GetInfo(&file_info)) {
1115 load_manager->ReportLoadError(
1116 PP_NACL_ERROR_PNACL_RESOURCE_FETCH,
1117 std::string("GetPNaClResourceInfo, GetFileInfo failed for: ") +
1118 kFilename);
1119 return PP_FALSE;
1122 if (file_info.size > 1 << 20) {
1123 load_manager->ReportLoadError(
1124 PP_NACL_ERROR_PNACL_RESOURCE_FETCH,
1125 std::string("GetPNaClResourceInfo, file too large: ") + kFilename);
1126 return PP_FALSE;
1129 scoped_ptr<char[]> buffer(new char[file_info.size + 1]);
1130 if (buffer.get() == NULL) {
1131 load_manager->ReportLoadError(
1132 PP_NACL_ERROR_PNACL_RESOURCE_FETCH,
1133 std::string("GetPNaClResourceInfo, couldn't allocate for: ") +
1134 kFilename);
1135 return PP_FALSE;
1138 int rc = file.Read(0, buffer.get(), file_info.size);
1139 if (rc < 0) {
1140 load_manager->ReportLoadError(
1141 PP_NACL_ERROR_PNACL_RESOURCE_FETCH,
1142 std::string("GetPNaClResourceInfo, reading failed for: ") + kFilename);
1143 return PP_FALSE;
1146 // Null-terminate the bytes we we read from the file.
1147 buffer.get()[rc] = 0;
1149 // Expect the JSON file to contain a top-level object (dictionary).
1150 Json::Reader json_reader;
1151 Json::Value json_data;
1152 if (!json_reader.parse(buffer.get(), json_data)) {
1153 load_manager->ReportLoadError(
1154 PP_NACL_ERROR_PNACL_RESOURCE_FETCH,
1155 std::string("Parsing resource info failed: JSON parse error: ") +
1156 json_reader.getFormattedErrorMessages());
1157 return PP_FALSE;
1160 if (!json_data.isObject()) {
1161 load_manager->ReportLoadError(
1162 PP_NACL_ERROR_PNACL_RESOURCE_FETCH,
1163 "Parsing resource info failed: Malformed JSON dictionary");
1164 return PP_FALSE;
1167 if (json_data.isMember("pnacl-llc-name")) {
1168 Json::Value json_name = json_data["pnacl-llc-name"];
1169 if (json_name.isString()) {
1170 std::string llc_tool_name_str = json_name.asString();
1171 *llc_tool_name = ppapi::StringVar::StringToPPVar(llc_tool_name_str);
1175 if (json_data.isMember("pnacl-ld-name")) {
1176 Json::Value json_name = json_data["pnacl-ld-name"];
1177 if (json_name.isString()) {
1178 std::string ld_tool_name_str = json_name.asString();
1179 *ld_tool_name = ppapi::StringVar::StringToPPVar(ld_tool_name_str);
1182 return PP_TRUE;
1185 PP_Var GetCpuFeatureAttrs() {
1186 return ppapi::StringVar::StringToPPVar(GetCpuFeatures());
1189 void PostMessageToJavaScriptMainThread(PP_Instance instance,
1190 const std::string& message) {
1191 content::PepperPluginInstance* plugin_instance =
1192 content::PepperPluginInstance::Get(instance);
1193 if (plugin_instance) {
1194 PP_Var message_var = ppapi::StringVar::StringToPPVar(message);
1195 plugin_instance->PostMessageToJavaScript(message_var);
1196 ppapi::PpapiGlobals::Get()->GetVarTracker()->ReleaseVar(message_var);
1200 void PostMessageToJavaScript(PP_Instance instance, const char* message) {
1201 ppapi::PpapiGlobals::Get()->GetMainThreadMessageLoop()->PostTask(
1202 FROM_HERE,
1203 base::Bind(&PostMessageToJavaScriptMainThread,
1204 instance,
1205 std::string(message)));
1208 // Encapsulates some of the state for a call to DownloadNexe to prevent
1209 // argument lists from getting too long.
1210 struct DownloadNexeRequest {
1211 PP_Instance instance;
1212 std::string url;
1213 PP_CompletionCallback callback;
1214 base::Time start_time;
1217 // A utility class to ensure that we don't send progress events more often than
1218 // every 10ms for a given file.
1219 class ProgressEventRateLimiter {
1220 public:
1221 explicit ProgressEventRateLimiter(PP_Instance instance)
1222 : instance_(instance) { }
1224 void ReportProgress(const std::string& url,
1225 int64_t total_bytes_received,
1226 int64_t total_bytes_to_be_received) {
1227 base::Time now = base::Time::Now();
1228 if (now - last_event_ > base::TimeDelta::FromMilliseconds(10)) {
1229 DispatchProgressEvent(instance_,
1230 ProgressEvent(PP_NACL_EVENT_PROGRESS,
1231 url,
1232 total_bytes_to_be_received >= 0,
1233 total_bytes_received,
1234 total_bytes_to_be_received));
1235 last_event_ = now;
1239 private:
1240 PP_Instance instance_;
1241 base::Time last_event_;
1244 void DownloadNexeCompletion(const DownloadNexeRequest& request,
1245 PP_NaClFileInfo* out_file_info,
1246 FileDownloader::Status status,
1247 base::File target_file,
1248 int http_status);
1250 void DownloadNexe(PP_Instance instance,
1251 const char* url,
1252 PP_NaClFileInfo* out_file_info,
1253 PP_CompletionCallback callback) {
1254 CHECK(url);
1255 CHECK(out_file_info);
1256 DownloadNexeRequest request;
1257 request.instance = instance;
1258 request.url = url;
1259 request.callback = callback;
1260 request.start_time = base::Time::Now();
1262 // Try the fast path for retrieving the file first.
1263 PP_FileHandle handle = OpenNaClExecutable(instance,
1264 url,
1265 &out_file_info->token_lo,
1266 &out_file_info->token_hi);
1267 if (handle != PP_kInvalidFileHandle) {
1268 DownloadNexeCompletion(request,
1269 out_file_info,
1270 FileDownloader::SUCCESS,
1271 base::File(handle),
1272 200);
1273 return;
1276 // The fast path didn't work, we'll fetch the file using URLLoader and write
1277 // it to local storage.
1278 base::File target_file(CreateTemporaryFile(instance));
1279 GURL gurl(url);
1281 content::PepperPluginInstance* plugin_instance =
1282 content::PepperPluginInstance::Get(instance);
1283 if (!plugin_instance) {
1284 ppapi::PpapiGlobals::Get()->GetMainThreadMessageLoop()->PostTask(
1285 FROM_HERE,
1286 base::Bind(callback.func, callback.user_data,
1287 static_cast<int32_t>(PP_ERROR_FAILED)));
1289 const blink::WebDocument& document =
1290 plugin_instance->GetContainer()->element().document();
1291 scoped_ptr<blink::WebURLLoader> url_loader(
1292 CreateWebURLLoader(document, gurl));
1293 blink::WebURLRequest url_request = CreateWebURLRequest(document, gurl);
1295 ProgressEventRateLimiter* tracker = new ProgressEventRateLimiter(instance);
1297 // FileDownloader deletes itself after invoking DownloadNexeCompletion.
1298 FileDownloader* file_downloader = new FileDownloader(
1299 url_loader.Pass(),
1300 target_file.Pass(),
1301 base::Bind(&DownloadNexeCompletion, request, out_file_info),
1302 base::Bind(&ProgressEventRateLimiter::ReportProgress,
1303 base::Owned(tracker), std::string(url)));
1304 file_downloader->Load(url_request);
1307 void DownloadNexeCompletion(const DownloadNexeRequest& request,
1308 PP_NaClFileInfo* out_file_info,
1309 FileDownloader::Status status,
1310 base::File target_file,
1311 int http_status) {
1312 int32_t pp_error = FileDownloaderToPepperError(status);
1313 int64_t bytes_read = -1;
1314 if (pp_error == PP_OK && target_file.IsValid()) {
1315 base::File::Info info;
1316 if (target_file.GetInfo(&info))
1317 bytes_read = info.size;
1320 if (bytes_read == -1) {
1321 target_file.Close();
1322 pp_error = PP_ERROR_FAILED;
1325 base::TimeDelta download_time = base::Time::Now() - request.start_time;
1327 NexeLoadManager* load_manager = GetNexeLoadManager(request.instance);
1328 if (load_manager) {
1329 load_manager->NexeFileDidOpen(pp_error,
1330 target_file,
1331 http_status,
1332 bytes_read,
1333 request.url,
1334 download_time);
1337 if (pp_error == PP_OK && target_file.IsValid())
1338 out_file_info->handle = target_file.TakePlatformFile();
1339 else
1340 out_file_info->handle = PP_kInvalidFileHandle;
1342 request.callback.func(request.callback.user_data, pp_error);
1345 void DownloadFileCompletion(
1346 const DownloadFileCallback& callback,
1347 FileDownloader::Status status,
1348 base::File file,
1349 int http_status) {
1350 int32_t pp_error = FileDownloaderToPepperError(status);
1351 PP_NaClFileInfo file_info;
1352 if (pp_error == PP_OK) {
1353 file_info.handle = file.TakePlatformFile();
1354 file_info.token_lo = 0;
1355 file_info.token_hi = 0;
1356 } else {
1357 file_info = kInvalidNaClFileInfo;
1360 callback.Run(pp_error, file_info);
1363 void DownloadFile(PP_Instance instance,
1364 const std::string& url,
1365 const DownloadFileCallback& callback) {
1366 DCHECK(ppapi::PpapiGlobals::Get()->GetMainThreadMessageLoop()->
1367 BelongsToCurrentThread());
1369 NexeLoadManager* load_manager = GetNexeLoadManager(instance);
1370 DCHECK(load_manager);
1371 if (!load_manager) {
1372 base::MessageLoop::current()->PostTask(
1373 FROM_HERE,
1374 base::Bind(callback,
1375 static_cast<int32_t>(PP_ERROR_FAILED),
1376 kInvalidNaClFileInfo));
1377 return;
1380 // Handle special PNaCl support files which are installed on the user's
1381 // machine.
1382 if (url.find(kPNaClTranslatorBaseUrl, 0) == 0) {
1383 PP_NaClFileInfo file_info = kInvalidNaClFileInfo;
1384 PP_FileHandle handle = GetReadonlyPnaclFd(url.c_str(),
1385 false /* is_executable */,
1386 &file_info.token_lo,
1387 &file_info.token_hi);
1388 if (handle == PP_kInvalidFileHandle) {
1389 base::MessageLoop::current()->PostTask(
1390 FROM_HERE,
1391 base::Bind(callback,
1392 static_cast<int32_t>(PP_ERROR_FAILED),
1393 kInvalidNaClFileInfo));
1394 return;
1396 file_info.handle = handle;
1397 base::MessageLoop::current()->PostTask(
1398 FROM_HERE,
1399 base::Bind(callback, static_cast<int32_t>(PP_OK), file_info));
1400 return;
1403 // We have to ensure that this url resolves relative to the plugin base url
1404 // before downloading it.
1405 const GURL& test_gurl = load_manager->plugin_base_url().Resolve(url);
1406 if (!test_gurl.is_valid()) {
1407 base::MessageLoop::current()->PostTask(
1408 FROM_HERE,
1409 base::Bind(callback,
1410 static_cast<int32_t>(PP_ERROR_FAILED),
1411 kInvalidNaClFileInfo));
1412 return;
1415 // Try the fast path for retrieving the file first.
1416 uint64_t file_token_lo = 0;
1417 uint64_t file_token_hi = 0;
1418 PP_FileHandle file_handle = OpenNaClExecutable(instance,
1419 url.c_str(),
1420 &file_token_lo,
1421 &file_token_hi);
1422 if (file_handle != PP_kInvalidFileHandle) {
1423 PP_NaClFileInfo file_info;
1424 file_info.handle = file_handle;
1425 file_info.token_lo = file_token_lo;
1426 file_info.token_hi = file_token_hi;
1427 base::MessageLoop::current()->PostTask(
1428 FROM_HERE,
1429 base::Bind(callback, static_cast<int32_t>(PP_OK), file_info));
1430 return;
1433 // The fast path didn't work, we'll fetch the file using URLLoader and write
1434 // it to local storage.
1435 base::File target_file(CreateTemporaryFile(instance));
1436 GURL gurl(url);
1438 content::PepperPluginInstance* plugin_instance =
1439 content::PepperPluginInstance::Get(instance);
1440 if (!plugin_instance) {
1441 base::MessageLoop::current()->PostTask(
1442 FROM_HERE,
1443 base::Bind(callback,
1444 static_cast<int32_t>(PP_ERROR_FAILED),
1445 kInvalidNaClFileInfo));
1447 const blink::WebDocument& document =
1448 plugin_instance->GetContainer()->element().document();
1449 scoped_ptr<blink::WebURLLoader> url_loader(
1450 CreateWebURLLoader(document, gurl));
1451 blink::WebURLRequest url_request = CreateWebURLRequest(document, gurl);
1453 ProgressEventRateLimiter* tracker = new ProgressEventRateLimiter(instance);
1455 // FileDownloader deletes itself after invoking DownloadNexeCompletion.
1456 FileDownloader* file_downloader = new FileDownloader(
1457 url_loader.Pass(),
1458 target_file.Pass(),
1459 base::Bind(&DownloadFileCompletion, callback),
1460 base::Bind(&ProgressEventRateLimiter::ReportProgress,
1461 base::Owned(tracker), std::string(url)));
1462 file_downloader->Load(url_request);
1465 void ReportSelLdrStatus(PP_Instance instance,
1466 int32_t load_status,
1467 int32_t max_status) {
1468 HistogramEnumerate("NaCl.LoadStatus.SelLdr", load_status, max_status);
1469 NexeLoadManager* load_manager = GetNexeLoadManager(instance);
1470 DCHECK(load_manager);
1471 if (!load_manager)
1472 return;
1474 // Gather data to see if being installed changes load outcomes.
1475 const char* name = load_manager->is_installed() ?
1476 "NaCl.LoadStatus.SelLdr.InstalledApp" :
1477 "NaCl.LoadStatus.SelLdr.NotInstalledApp";
1478 HistogramEnumerate(name, load_status, max_status);
1481 void LogTranslateTime(const char* histogram_name,
1482 int64_t time_in_us) {
1483 ppapi::PpapiGlobals::Get()->GetMainThreadMessageLoop()->PostTask(
1484 FROM_HERE,
1485 base::Bind(&HistogramTimeTranslation,
1486 std::string(histogram_name),
1487 time_in_us / 1000));
1490 void DidOpenManifestEntry(PP_NaClFileInfo* out_file_info,
1491 PP_CompletionCallback callback,
1492 int32_t pp_error,
1493 const PP_NaClFileInfo& file_info) {
1494 if (pp_error == PP_OK)
1495 *out_file_info = file_info;
1496 callback.func(callback.user_data, pp_error);
1499 void OpenManifestEntry(PP_Instance instance,
1500 PP_Bool is_helper_process,
1501 const char* key,
1502 PP_NaClFileInfo* out_file_info,
1503 PP_CompletionCallback callback) {
1504 std::string url;
1505 PP_PNaClOptions pnacl_options;
1506 pnacl_options.translate = PP_FALSE;
1507 pnacl_options.is_debug = PP_FALSE;
1508 pnacl_options.opt_level = 2;
1509 if (!ManifestResolveKey(instance,
1510 PP_ToBool(is_helper_process),
1511 key,
1512 &url,
1513 &pnacl_options)) {
1514 PostPPCompletionCallback(callback, PP_ERROR_FAILED);
1517 // TODO(teravest): Make a type like PP_NaClFileInfo to use for DownloadFile
1518 // that would close the file handle on destruction.
1519 DownloadFile(instance, url,
1520 base::Bind(&DidOpenManifestEntry, out_file_info, callback));
1523 void SetPNaClStartTime(PP_Instance instance) {
1524 NexeLoadManager* load_manager = GetNexeLoadManager(instance);
1525 if (load_manager)
1526 load_manager->set_pnacl_start_time(base::Time::Now());
1529 class PexeDownloader : public blink::WebURLLoaderClient {
1530 public:
1531 PexeDownloader(PP_Instance instance,
1532 scoped_ptr<blink::WebURLLoader> url_loader,
1533 const std::string& pexe_url,
1534 int32_t pexe_opt_level,
1535 const PPP_PexeStreamHandler* stream_handler,
1536 void* stream_handler_user_data)
1537 : instance_(instance),
1538 url_loader_(url_loader.Pass()),
1539 pexe_url_(pexe_url),
1540 pexe_opt_level_(pexe_opt_level),
1541 stream_handler_(stream_handler),
1542 stream_handler_user_data_(stream_handler_user_data),
1543 success_(false),
1544 expected_content_length_(-1),
1545 weak_factory_(this) { }
1547 void Load(const blink::WebURLRequest& request) {
1548 url_loader_->loadAsynchronously(request, this);
1551 private:
1552 virtual void didReceiveResponse(blink::WebURLLoader* loader,
1553 const blink::WebURLResponse& response) {
1554 success_ = (response.httpStatusCode() == 200);
1555 if (!success_)
1556 return;
1558 expected_content_length_ = response.expectedContentLength();
1560 // Defer loading after receiving headers. This is because we may already
1561 // have a cached translated nexe, so check for that now.
1562 url_loader_->setDefersLoading(true);
1564 std::string etag = response.httpHeaderField("etag").utf8();
1565 std::string last_modified =
1566 response.httpHeaderField("last-modified").utf8();
1567 base::Time last_modified_time;
1568 base::Time::FromString(last_modified.c_str(), &last_modified_time);
1570 bool has_no_store_header = false;
1571 std::string cache_control =
1572 response.httpHeaderField("cache-control").utf8();
1574 std::vector<std::string> values;
1575 base::SplitString(cache_control, ',', &values);
1576 for (std::vector<std::string>::const_iterator it = values.begin();
1577 it != values.end();
1578 ++it) {
1579 if (StringToLowerASCII(*it) == "no-store")
1580 has_no_store_header = true;
1583 GetNexeFd(instance_,
1584 pexe_url_,
1585 pexe_opt_level_,
1586 last_modified_time,
1587 etag,
1588 has_no_store_header,
1589 base::Bind(&PexeDownloader::didGetNexeFd,
1590 weak_factory_.GetWeakPtr()));
1593 virtual void didGetNexeFd(int32_t pp_error,
1594 bool cache_hit,
1595 PP_FileHandle file_handle) {
1596 if (cache_hit) {
1597 stream_handler_->DidCacheHit(stream_handler_user_data_, file_handle);
1599 // We delete the PexeDownloader at this point since we successfully got a
1600 // cached, translated nexe.
1601 delete this;
1602 return;
1604 stream_handler_->DidCacheMiss(stream_handler_user_data_,
1605 expected_content_length_);
1607 // No translated nexe was found in the cache, so we should download the
1608 // file to start streaming it.
1609 url_loader_->setDefersLoading(false);
1612 virtual void didReceiveData(blink::WebURLLoader* loader,
1613 const char* data,
1614 int data_length,
1615 int encoded_data_length) {
1616 // Stream the data we received to the stream callback.
1617 stream_handler_->DidStreamData(stream_handler_user_data_,
1618 data,
1619 data_length);
1622 virtual void didFinishLoading(blink::WebURLLoader* loader,
1623 double finish_time,
1624 int64_t total_encoded_data_length) {
1625 int32_t result = success_ ? PP_OK : PP_ERROR_FAILED;
1626 stream_handler_->DidFinishStream(stream_handler_user_data_, result);
1627 delete this;
1630 virtual void didFail(blink::WebURLLoader* loader,
1631 const blink::WebURLError& error) {
1632 success_ = false;
1635 PP_Instance instance_;
1636 scoped_ptr<blink::WebURLLoader> url_loader_;
1637 std::string pexe_url_;
1638 int32_t pexe_opt_level_;
1639 const PPP_PexeStreamHandler* stream_handler_;
1640 void* stream_handler_user_data_;
1641 bool success_;
1642 int64_t expected_content_length_;
1643 base::WeakPtrFactory<PexeDownloader> weak_factory_;
1646 void StreamPexe(PP_Instance instance,
1647 const char* pexe_url,
1648 int32_t opt_level,
1649 const PPP_PexeStreamHandler* handler,
1650 void* handler_user_data) {
1651 content::PepperPluginInstance* plugin_instance =
1652 content::PepperPluginInstance::Get(instance);
1653 if (!plugin_instance) {
1654 base::MessageLoop::current()->PostTask(
1655 FROM_HERE,
1656 base::Bind(handler->DidFinishStream,
1657 handler_user_data,
1658 static_cast<int32_t>(PP_ERROR_FAILED)));
1659 return;
1662 GURL gurl(pexe_url);
1663 const blink::WebDocument& document =
1664 plugin_instance->GetContainer()->element().document();
1665 scoped_ptr<blink::WebURLLoader> url_loader(
1666 CreateWebURLLoader(document, gurl));
1667 PexeDownloader* downloader = new PexeDownloader(instance,
1668 url_loader.Pass(),
1669 pexe_url,
1670 opt_level,
1671 handler,
1672 handler_user_data);
1674 blink::WebURLRequest url_request = CreateWebURLRequest(document, gurl);
1675 // Mark the request as requesting a PNaCl bitcode file,
1676 // so that component updater can detect this user action.
1677 url_request.addHTTPHeaderField(
1678 blink::WebString::fromUTF8("Accept"),
1679 blink::WebString::fromUTF8("application/x-pnacl, */*"));
1680 downloader->Load(url_request);
1683 const PPB_NaCl_Private nacl_interface = {
1684 &LaunchSelLdr,
1685 &StartPpapiProxy,
1686 &UrandomFD,
1687 &Are3DInterfacesDisabled,
1688 &BrokerDuplicateHandle,
1689 &GetReadExecPnaclFd,
1690 &CreateTemporaryFile,
1691 &GetNumberOfProcessors,
1692 &PPIsNonSFIModeEnabled,
1693 &ReportTranslationFinished,
1694 &DispatchEvent,
1695 &ReportLoadSuccess,
1696 &ReportLoadError,
1697 &ReportLoadAbort,
1698 &NexeDidCrash,
1699 &InstanceCreated,
1700 &InstanceDestroyed,
1701 &NaClDebugEnabledForURL,
1702 &GetSandboxArch,
1703 &LogToConsole,
1704 &GetNaClReadyState,
1705 &GetExitStatus,
1706 &SetExitStatus,
1707 &Vlog,
1708 &InitializePlugin,
1709 &GetNexeSize,
1710 &RequestNaClManifest,
1711 &GetManifestBaseURL,
1712 &ProcessNaClManifest,
1713 &DevInterfacesEnabled,
1714 &ManifestGetProgramURL,
1715 &GetPNaClResourceInfo,
1716 &GetCpuFeatureAttrs,
1717 &PostMessageToJavaScript,
1718 &DownloadNexe,
1719 &ReportSelLdrStatus,
1720 &LogTranslateTime,
1721 &OpenManifestEntry,
1722 &SetPNaClStartTime,
1723 &StreamPexe
1726 } // namespace
1728 const PPB_NaCl_Private* GetNaClPrivateInterface() {
1729 return &nacl_interface;
1732 } // namespace nacl