Updating trunk VERSION from 2139.0 to 2140.0
[chromium-blink-merge.git] / components / nacl / renderer / ppb_nacl_private_impl.cc
blob5bc480b111b367fa89dca2c3c7c0091e8c24a5ec
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/cpu.h"
15 #include "base/files/file.h"
16 #include "base/lazy_instance.h"
17 #include "base/logging.h"
18 #include "base/rand_util.h"
19 #include "base/strings/string_split.h"
20 #include "base/strings/string_util.h"
21 #include "components/nacl/common/nacl_host_messages.h"
22 #include "components/nacl/common/nacl_messages.h"
23 #include "components/nacl/common/nacl_nonsfi_util.h"
24 #include "components/nacl/common/nacl_switches.h"
25 #include "components/nacl/common/nacl_types.h"
26 #include "components/nacl/renderer/file_downloader.h"
27 #include "components/nacl/renderer/histogram.h"
28 #include "components/nacl/renderer/json_manifest.h"
29 #include "components/nacl/renderer/manifest_downloader.h"
30 #include "components/nacl/renderer/manifest_service_channel.h"
31 #include "components/nacl/renderer/nexe_load_manager.h"
32 #include "components/nacl/renderer/platform_info.h"
33 #include "components/nacl/renderer/pnacl_translation_resource_host.h"
34 #include "components/nacl/renderer/progress_event.h"
35 #include "components/nacl/renderer/trusted_plugin_channel.h"
36 #include "content/public/common/content_client.h"
37 #include "content/public/common/content_switches.h"
38 #include "content/public/common/sandbox_init.h"
39 #include "content/public/renderer/pepper_plugin_instance.h"
40 #include "content/public/renderer/render_thread.h"
41 #include "content/public/renderer/render_view.h"
42 #include "content/public/renderer/renderer_ppapi_host.h"
43 #include "native_client/src/public/imc_types.h"
44 #include "net/base/data_url.h"
45 #include "net/base/net_errors.h"
46 #include "net/http/http_util.h"
47 #include "ppapi/c/pp_bool.h"
48 #include "ppapi/c/private/pp_file_handle.h"
49 #include "ppapi/shared_impl/ppapi_globals.h"
50 #include "ppapi/shared_impl/ppapi_permissions.h"
51 #include "ppapi/shared_impl/ppapi_preferences.h"
52 #include "ppapi/shared_impl/var.h"
53 #include "ppapi/shared_impl/var_tracker.h"
54 #include "ppapi/thunk/enter.h"
55 #include "third_party/WebKit/public/platform/WebURLLoader.h"
56 #include "third_party/WebKit/public/platform/WebURLResponse.h"
57 #include "third_party/WebKit/public/web/WebDocument.h"
58 #include "third_party/WebKit/public/web/WebElement.h"
59 #include "third_party/WebKit/public/web/WebLocalFrame.h"
60 #include "third_party/WebKit/public/web/WebPluginContainer.h"
61 #include "third_party/WebKit/public/web/WebSecurityOrigin.h"
62 #include "third_party/WebKit/public/web/WebURLLoaderOptions.h"
63 #include "third_party/jsoncpp/source/include/json/reader.h"
64 #include "third_party/jsoncpp/source/include/json/value.h"
66 namespace nacl {
67 namespace {
69 // The pseudo-architecture used to indicate portable native client.
70 const char* const kPortableArch = "portable";
72 // The base URL for resources used by the PNaCl translator processes.
73 const char* kPNaClTranslatorBaseUrl = "chrome://pnacl-translator/";
75 base::LazyInstance<scoped_refptr<PnaclTranslationResourceHost> >
76 g_pnacl_resource_host = LAZY_INSTANCE_INITIALIZER;
78 bool InitializePnaclResourceHost() {
79 // Must run on the main thread.
80 content::RenderThread* render_thread = content::RenderThread::Get();
81 if (!render_thread)
82 return false;
83 if (!g_pnacl_resource_host.Get().get()) {
84 g_pnacl_resource_host.Get() = new PnaclTranslationResourceHost(
85 render_thread->GetIOMessageLoopProxy());
86 render_thread->AddFilter(g_pnacl_resource_host.Get().get());
88 return true;
91 struct InstanceInfo {
92 InstanceInfo() : plugin_pid(base::kNullProcessId), plugin_child_id(0) {}
93 GURL url;
94 ppapi::PpapiPermissions permissions;
95 base::ProcessId plugin_pid;
96 int plugin_child_id;
97 IPC::ChannelHandle channel_handle;
100 typedef std::map<PP_Instance, InstanceInfo> InstanceInfoMap;
102 base::LazyInstance<InstanceInfoMap> g_instance_info =
103 LAZY_INSTANCE_INITIALIZER;
105 static const PP_NaClFileInfo kInvalidNaClFileInfo = {
106 PP_kInvalidFileHandle,
107 0, // token_lo
108 0, // token_hi
111 int GetRoutingID(PP_Instance instance) {
112 // Check that we are on the main renderer thread.
113 DCHECK(content::RenderThread::Get());
114 content::RendererPpapiHost *host =
115 content::RendererPpapiHost::GetForPPInstance(instance);
116 if (!host)
117 return 0;
118 return host->GetRoutingIDForWidget(instance);
121 // Returns whether the channel_handle is valid or not.
122 bool IsValidChannelHandle(const IPC::ChannelHandle& channel_handle) {
123 if (channel_handle.name.empty()) {
124 return false;
127 #if defined(OS_POSIX)
128 if (channel_handle.socket.fd == -1) {
129 return false;
131 #endif
133 return true;
136 void PostPPCompletionCallback(PP_CompletionCallback callback,
137 int32_t status) {
138 ppapi::PpapiGlobals::Get()->GetMainThreadMessageLoop()->PostTask(
139 FROM_HERE,
140 base::Bind(callback.func, callback.user_data, status));
143 bool ManifestResolveKey(PP_Instance instance,
144 bool is_helper_process,
145 const std::string& key,
146 std::string* full_url,
147 PP_PNaClOptions* pnacl_options);
149 typedef base::Callback<void(int32_t, const PP_NaClFileInfo&)>
150 DownloadFileCallback;
152 void DownloadFile(PP_Instance instance,
153 const std::string& url,
154 const DownloadFileCallback& callback);
156 PP_Bool StartPpapiProxy(PP_Instance instance);
158 // Thin adapter from PPP_ManifestService to ManifestServiceChannel::Delegate.
159 // Note that user_data is managed by the caller of LaunchSelLdr. Please see
160 // also PP_ManifestService's comment for more details about resource
161 // management.
162 class ManifestServiceProxy : public ManifestServiceChannel::Delegate {
163 public:
164 ManifestServiceProxy(PP_Instance pp_instance)
165 : pp_instance_(pp_instance) {
168 virtual ~ManifestServiceProxy() { }
170 virtual void StartupInitializationComplete() OVERRIDE {
171 if (StartPpapiProxy(pp_instance_) == PP_TRUE) {
172 JsonManifest* manifest = GetJsonManifest(pp_instance_);
173 NexeLoadManager* load_manager = NexeLoadManager::Get(pp_instance_);
174 if (load_manager && manifest) {
175 std::string full_url;
176 PP_PNaClOptions pnacl_options;
177 bool uses_nonsfi_mode;
178 JsonManifest::ErrorInfo error_info;
179 if (manifest->GetProgramURL(&full_url,
180 &pnacl_options,
181 &uses_nonsfi_mode,
182 &error_info)) {
183 int64_t nexe_size = load_manager->nexe_size();
184 load_manager->ReportLoadSuccess(full_url, nexe_size, nexe_size);
190 virtual void OpenResource(
191 const std::string& key,
192 const ManifestServiceChannel::OpenResourceCallback& callback) OVERRIDE {
193 DCHECK(ppapi::PpapiGlobals::Get()->GetMainThreadMessageLoop()->
194 BelongsToCurrentThread());
196 std::string url;
197 // TODO(teravest): Clean up pnacl_options logic in JsonManifest so we don't
198 // have to initialize it like this here.
199 PP_PNaClOptions pnacl_options;
200 pnacl_options.translate = PP_FALSE;
201 pnacl_options.is_debug = PP_FALSE;
202 pnacl_options.opt_level = 2;
203 if (!ManifestResolveKey(pp_instance_, false, key, &url, &pnacl_options)) {
204 base::MessageLoop::current()->PostTask(
205 FROM_HERE,
206 base::Bind(callback, base::Passed(base::File())));
207 return;
210 // We have to call DidDownloadFile, even if this object is destroyed, so
211 // that the handle inside PP_NaClFileInfo isn't leaked. This means that the
212 // callback passed to this function shouldn't have a weak pointer to an
213 // object either.
215 // TODO(teravest): Make a type like PP_NaClFileInfo to use for DownloadFile
216 // that would close the file handle on destruction.
217 DownloadFile(pp_instance_, url,
218 base::Bind(&ManifestServiceProxy::DidDownloadFile, callback));
221 private:
222 static void DidDownloadFile(
223 ManifestServiceChannel::OpenResourceCallback callback,
224 int32_t pp_error,
225 const PP_NaClFileInfo& file_info) {
226 if (pp_error != PP_OK) {
227 callback.Run(base::File());
228 return;
230 callback.Run(base::File(file_info.handle));
233 PP_Instance pp_instance_;
234 DISALLOW_COPY_AND_ASSIGN(ManifestServiceProxy);
237 blink::WebURLLoader* CreateWebURLLoader(const blink::WebDocument& document,
238 const GURL& gurl) {
239 blink::WebURLLoaderOptions options;
240 options.untrustedHTTP = true;
242 // Options settings here follow the original behavior in the trusted
243 // plugin and PepperURLLoaderHost.
244 if (document.securityOrigin().canRequest(gurl)) {
245 options.allowCredentials = true;
246 } else {
247 // Allow CORS.
248 options.crossOriginRequestPolicy =
249 blink::WebURLLoaderOptions::CrossOriginRequestPolicyUseAccessControl;
251 return document.frame()->createAssociatedURLLoader(options);
254 blink::WebURLRequest CreateWebURLRequest(const blink::WebDocument& document,
255 const GURL& gurl) {
256 blink::WebURLRequest request;
257 request.initialize();
258 request.setURL(gurl);
259 request.setFirstPartyForCookies(document.firstPartyForCookies());
260 return request;
263 int32_t FileDownloaderToPepperError(FileDownloader::Status status) {
264 switch (status) {
265 case FileDownloader::SUCCESS:
266 return PP_OK;
267 case FileDownloader::ACCESS_DENIED:
268 return PP_ERROR_NOACCESS;
269 case FileDownloader::FAILED:
270 return PP_ERROR_FAILED;
271 // No default case, to catch unhandled Status values.
273 return PP_ERROR_FAILED;
276 // Launch NaCl's sel_ldr process.
277 void LaunchSelLdr(PP_Instance instance,
278 PP_Bool main_service_runtime,
279 const char* alleged_url,
280 const PP_NaClFileInfo* nexe_file_info,
281 PP_Bool uses_irt,
282 PP_Bool uses_ppapi,
283 PP_Bool uses_nonsfi_mode,
284 PP_Bool enable_ppapi_dev,
285 PP_Bool enable_dyncode_syscalls,
286 PP_Bool enable_exception_handling,
287 PP_Bool enable_crash_throttling,
288 void* imc_handle,
289 PP_CompletionCallback callback) {
290 CHECK(ppapi::PpapiGlobals::Get()->GetMainThreadMessageLoop()->
291 BelongsToCurrentThread());
293 // Create the manifest service proxy here, so on error case, it will be
294 // destructed (without passing it to ManifestServiceChannel).
295 scoped_ptr<ManifestServiceChannel::Delegate> manifest_service_proxy(
296 new ManifestServiceProxy(instance));
298 FileDescriptor result_socket;
299 IPC::Sender* sender = content::RenderThread::Get();
300 DCHECK(sender);
301 int routing_id = 0;
302 // If the nexe uses ppapi APIs, we need a routing ID.
303 // To get the routing ID, we must be on the main thread.
304 // Some nexes do not use ppapi and launch from the background thread,
305 // so those nexes can skip finding a routing_id.
306 if (uses_ppapi) {
307 routing_id = GetRoutingID(instance);
308 if (!routing_id) {
309 if (nexe_file_info->handle != PP_kInvalidFileHandle) {
310 base::File closer(nexe_file_info->handle);
312 ppapi::PpapiGlobals::Get()->GetMainThreadMessageLoop()->PostTask(
313 FROM_HERE,
314 base::Bind(callback.func, callback.user_data,
315 static_cast<int32_t>(PP_ERROR_FAILED)));
316 return;
320 InstanceInfo instance_info;
321 instance_info.url = GURL(alleged_url);
323 uint32_t perm_bits = ppapi::PERMISSION_NONE;
324 // Conditionally block 'Dev' interfaces. We do this for the NaCl process, so
325 // it's clearer to developers when they are using 'Dev' inappropriately. We
326 // must also check on the trusted side of the proxy.
327 if (enable_ppapi_dev)
328 perm_bits |= ppapi::PERMISSION_DEV;
329 instance_info.permissions =
330 ppapi::PpapiPermissions::GetForCommandLine(perm_bits);
331 std::string error_message_string;
332 NaClLaunchResult launch_result;
334 IPC::PlatformFileForTransit nexe_for_transit =
335 IPC::InvalidPlatformFileForTransit();
336 #if defined(OS_POSIX)
337 if (nexe_file_info->handle != PP_kInvalidFileHandle)
338 nexe_for_transit = base::FileDescriptor(nexe_file_info->handle, true);
339 #elif defined(OS_WIN)
340 // Duplicate the handle on the browser side instead of the renderer.
341 // This is because BrokerGetFileForProcess isn't part of content/public, and
342 // it's simpler to do the duplication in the browser anyway.
343 nexe_for_transit = nexe_file_info->handle;
344 #else
345 #error Unsupported target platform.
346 #endif
347 if (!sender->Send(new NaClHostMsg_LaunchNaCl(
348 NaClLaunchParams(
349 instance_info.url.spec(),
350 nexe_for_transit,
351 nexe_file_info->token_lo,
352 nexe_file_info->token_hi,
353 routing_id,
354 perm_bits,
355 PP_ToBool(uses_irt),
356 PP_ToBool(uses_nonsfi_mode),
357 PP_ToBool(enable_dyncode_syscalls),
358 PP_ToBool(enable_exception_handling),
359 PP_ToBool(enable_crash_throttling)),
360 &launch_result,
361 &error_message_string))) {
362 ppapi::PpapiGlobals::Get()->GetMainThreadMessageLoop()->PostTask(
363 FROM_HERE,
364 base::Bind(callback.func, callback.user_data,
365 static_cast<int32_t>(PP_ERROR_FAILED)));
366 return;
369 if (!error_message_string.empty()) {
370 if (PP_ToBool(main_service_runtime)) {
371 NexeLoadManager* load_manager = NexeLoadManager::Get(instance);
372 if (load_manager) {
373 load_manager->ReportLoadError(PP_NACL_ERROR_SEL_LDR_LAUNCH,
374 "ServiceRuntime: failed to start",
375 error_message_string);
378 ppapi::PpapiGlobals::Get()->GetMainThreadMessageLoop()->PostTask(
379 FROM_HERE,
380 base::Bind(callback.func, callback.user_data,
381 static_cast<int32_t>(PP_ERROR_FAILED)));
382 return;
384 result_socket = launch_result.imc_channel_handle;
385 instance_info.channel_handle = launch_result.ppapi_ipc_channel_handle;
386 instance_info.plugin_pid = launch_result.plugin_pid;
387 instance_info.plugin_child_id = launch_result.plugin_child_id;
389 // Don't save instance_info if channel handle is invalid.
390 if (IsValidChannelHandle(instance_info.channel_handle))
391 g_instance_info.Get()[instance] = instance_info;
393 *(static_cast<NaClHandle*>(imc_handle)) = ToNativeHandle(result_socket);
395 NexeLoadManager* load_manager = NexeLoadManager::Get(instance);
396 DCHECK(load_manager);
397 if (!load_manager) {
398 PostPPCompletionCallback(callback, PP_ERROR_FAILED);
399 base::SharedMemory::CloseHandle(launch_result.crash_info_shmem_handle);
400 return;
403 // Store the crash information shared memory handle.
404 load_manager->set_crash_info_shmem_handle(
405 launch_result.crash_info_shmem_handle);
407 // Create the trusted plugin channel.
408 if (IsValidChannelHandle(launch_result.trusted_ipc_channel_handle)) {
409 bool report_exit_status = PP_ToBool(main_service_runtime);
410 scoped_ptr<TrustedPluginChannel> trusted_plugin_channel(
411 new TrustedPluginChannel(
412 load_manager,
413 launch_result.trusted_ipc_channel_handle,
414 content::RenderThread::Get()->GetShutdownEvent(),
415 report_exit_status));
416 load_manager->set_trusted_plugin_channel(trusted_plugin_channel.Pass());
417 } else {
418 PostPPCompletionCallback(callback, PP_ERROR_FAILED);
419 return;
422 // Create the manifest service handle as well.
423 // For security hardening, disable the IPCs for open_resource() when they
424 // aren't needed. PNaCl doesn't expose open_resource(), and the new
425 // open_resource() IPCs are currently only used for Non-SFI NaCl so far,
426 // not SFI NaCl. Note that enable_dyncode_syscalls is true if and only if
427 // the plugin is a non-PNaCl plugin.
428 if (load_manager &&
429 enable_dyncode_syscalls &&
430 uses_nonsfi_mode &&
431 IsValidChannelHandle(
432 launch_result.manifest_service_ipc_channel_handle)) {
433 scoped_ptr<ManifestServiceChannel> manifest_service_channel(
434 new ManifestServiceChannel(
435 launch_result.manifest_service_ipc_channel_handle,
436 base::Bind(&PostPPCompletionCallback, callback),
437 manifest_service_proxy.Pass(),
438 content::RenderThread::Get()->GetShutdownEvent()));
439 load_manager->set_manifest_service_channel(
440 manifest_service_channel.Pass());
441 } else {
442 // Currently, manifest service works only on linux/non-SFI mode.
443 // On other platforms, the socket will not be created, and thus this
444 // condition needs to be handled as success.
445 PostPPCompletionCallback(callback, PP_OK);
449 PP_Bool StartPpapiProxy(PP_Instance instance) {
450 NexeLoadManager* load_manager = NexeLoadManager::Get(instance);
451 DCHECK(load_manager);
452 if (!load_manager)
453 return PP_FALSE;
455 content::PepperPluginInstance* plugin_instance =
456 content::PepperPluginInstance::Get(instance);
457 if (!plugin_instance) {
458 DLOG(ERROR) << "GetInstance() failed";
459 return PP_FALSE;
462 InstanceInfoMap& map = g_instance_info.Get();
463 InstanceInfoMap::iterator it = map.find(instance);
464 if (it == map.end()) {
465 DLOG(ERROR) << "Could not find instance ID";
466 return PP_FALSE;
468 InstanceInfo instance_info = it->second;
469 map.erase(it);
471 PP_ExternalPluginResult result = plugin_instance->SwitchToOutOfProcessProxy(
472 base::FilePath().AppendASCII(instance_info.url.spec()),
473 instance_info.permissions,
474 instance_info.channel_handle,
475 instance_info.plugin_pid,
476 instance_info.plugin_child_id);
478 if (result == PP_EXTERNAL_PLUGIN_OK) {
479 // Log the amound of time that has passed between the trusted plugin being
480 // initialized and the untrusted plugin being initialized. This is
481 // (roughly) the cost of using NaCl, in terms of startup time.
482 load_manager->ReportStartupOverhead();
483 return PP_TRUE;
484 } else if (result == PP_EXTERNAL_PLUGIN_ERROR_MODULE) {
485 load_manager->ReportLoadError(PP_NACL_ERROR_START_PROXY_MODULE,
486 "could not initialize module.");
487 } else if (result == PP_EXTERNAL_PLUGIN_ERROR_INSTANCE) {
488 load_manager->ReportLoadError(PP_NACL_ERROR_START_PROXY_MODULE,
489 "could not create instance.");
491 return PP_FALSE;
494 int UrandomFD(void) {
495 #if defined(OS_POSIX)
496 return base::GetUrandomFD();
497 #else
498 return -1;
499 #endif
502 PP_Bool Are3DInterfacesDisabled() {
503 return PP_FromBool(CommandLine::ForCurrentProcess()->HasSwitch(
504 switches::kDisable3DAPIs));
507 int32_t BrokerDuplicateHandle(PP_FileHandle source_handle,
508 uint32_t process_id,
509 PP_FileHandle* target_handle,
510 uint32_t desired_access,
511 uint32_t options) {
512 #if defined(OS_WIN)
513 return content::BrokerDuplicateHandle(source_handle, process_id,
514 target_handle, desired_access,
515 options);
516 #else
517 return 0;
518 #endif
521 // Convert a URL to a filename for GetReadonlyPnaclFd.
522 // Must be kept in sync with PnaclCanOpenFile() in
523 // components/nacl/browser/nacl_file_host.cc.
524 std::string PnaclComponentURLToFilename(const std::string& url) {
525 // PNaCl component URLs aren't arbitrary URLs; they are always either
526 // generated from ManifestResolveKey or PnaclResources::ReadResourceInfo.
527 // So, it's safe to just use string parsing operations here instead of
528 // URL-parsing ones.
529 DCHECK(StartsWithASCII(url, kPNaClTranslatorBaseUrl, true));
530 std::string r = url.substr(std::string(kPNaClTranslatorBaseUrl).length());
532 // Use white-listed-chars.
533 size_t replace_pos;
534 static const char* white_list = "abcdefghijklmnopqrstuvwxyz0123456789_";
535 replace_pos = r.find_first_not_of(white_list);
536 while(replace_pos != std::string::npos) {
537 r = r.replace(replace_pos, 1, "_");
538 replace_pos = r.find_first_not_of(white_list);
540 return r;
543 PP_FileHandle GetReadonlyPnaclFd(const char* url,
544 bool is_executable,
545 uint64_t* nonce_lo,
546 uint64_t* nonce_hi) {
547 std::string filename = PnaclComponentURLToFilename(url);
548 IPC::PlatformFileForTransit out_fd = IPC::InvalidPlatformFileForTransit();
549 IPC::Sender* sender = content::RenderThread::Get();
550 DCHECK(sender);
551 if (!sender->Send(new NaClHostMsg_GetReadonlyPnaclFD(
552 std::string(filename), is_executable,
553 &out_fd, nonce_lo, nonce_hi))) {
554 return PP_kInvalidFileHandle;
556 if (out_fd == IPC::InvalidPlatformFileForTransit()) {
557 return PP_kInvalidFileHandle;
559 return IPC::PlatformFileForTransitToPlatformFile(out_fd);
562 void GetReadExecPnaclFd(const char* url,
563 PP_NaClFileInfo* out_file_info) {
564 *out_file_info = kInvalidNaClFileInfo;
565 out_file_info->handle = GetReadonlyPnaclFd(url, true /* is_executable */,
566 &out_file_info->token_lo,
567 &out_file_info->token_hi);
570 PP_FileHandle CreateTemporaryFile(PP_Instance instance) {
571 IPC::PlatformFileForTransit transit_fd = IPC::InvalidPlatformFileForTransit();
572 IPC::Sender* sender = content::RenderThread::Get();
573 DCHECK(sender);
574 if (!sender->Send(new NaClHostMsg_NaClCreateTemporaryFile(
575 &transit_fd))) {
576 return PP_kInvalidFileHandle;
579 if (transit_fd == IPC::InvalidPlatformFileForTransit()) {
580 return PP_kInvalidFileHandle;
583 return IPC::PlatformFileForTransitToPlatformFile(transit_fd);
586 int32_t GetNumberOfProcessors() {
587 IPC::Sender* sender = content::RenderThread::Get();
588 DCHECK(sender);
589 int32_t num_processors = 1;
590 return sender->Send(new NaClHostMsg_NaClGetNumProcessors(&num_processors)) ?
591 num_processors : 1;
594 PP_Bool PPIsNonSFIModeEnabled() {
595 return PP_FromBool(IsNonSFIModeEnabled());
598 void GetNexeFd(PP_Instance instance,
599 const std::string& pexe_url,
600 uint32_t opt_level,
601 const base::Time& last_modified_time,
602 const std::string& etag,
603 bool has_no_store_header,
604 base::Callback<void(int32_t, bool, PP_FileHandle)> callback) {
605 if (!InitializePnaclResourceHost()) {
606 ppapi::PpapiGlobals::Get()->GetMainThreadMessageLoop()->PostTask(
607 FROM_HERE,
608 base::Bind(callback,
609 static_cast<int32_t>(PP_ERROR_FAILED),
610 false,
611 PP_kInvalidFileHandle));
612 return;
615 PnaclCacheInfo cache_info;
616 cache_info.pexe_url = GURL(pexe_url);
617 // TODO(dschuff): Get this value from the pnacl json file after it
618 // rolls in from NaCl.
619 cache_info.abi_version = 1;
620 cache_info.opt_level = opt_level;
621 cache_info.last_modified = last_modified_time;
622 cache_info.etag = etag;
623 cache_info.has_no_store_header = has_no_store_header;
624 cache_info.sandbox_isa = GetSandboxArch();
625 cache_info.extra_flags = GetCpuFeatures();
627 g_pnacl_resource_host.Get()->RequestNexeFd(
628 GetRoutingID(instance),
629 instance,
630 cache_info,
631 callback);
634 void ReportTranslationFinished(PP_Instance instance,
635 PP_Bool success,
636 int32_t opt_level,
637 int64_t pexe_size,
638 int64_t compile_time_us) {
639 if (success == PP_TRUE) {
640 static const int32_t kUnknownOptLevel = 4;
641 if (opt_level < 0 || opt_level > 3)
642 opt_level = kUnknownOptLevel;
643 HistogramEnumerate("NaCl.Options.PNaCl.OptLevel",
644 opt_level,
645 kUnknownOptLevel + 1);
646 HistogramKBPerSec("NaCl.Perf.PNaClLoadTime.CompileKBPerSec",
647 pexe_size / 1024,
648 compile_time_us);
649 HistogramSizeKB("NaCl.Perf.Size.Pexe", pexe_size / 1024);
651 NexeLoadManager* load_manager = NexeLoadManager::Get(instance);
652 if (load_manager) {
653 base::TimeDelta total_time = base::Time::Now() -
654 load_manager->pnacl_start_time();
655 HistogramTimeTranslation("NaCl.Perf.PNaClLoadTime.TotalUncachedTime",
656 total_time.InMilliseconds());
657 HistogramKBPerSec("NaCl.Perf.PNaClLoadTime.TotalUncachedKBPerSec",
658 pexe_size / 1024,
659 total_time.InMicroseconds());
663 // If the resource host isn't initialized, don't try to do that here.
664 // Just return because something is already very wrong.
665 if (g_pnacl_resource_host.Get().get() == NULL)
666 return;
667 g_pnacl_resource_host.Get()->ReportTranslationFinished(instance, success);
670 PP_FileHandle OpenNaClExecutable(PP_Instance instance,
671 const char* file_url,
672 uint64_t* nonce_lo,
673 uint64_t* nonce_hi) {
674 // Fast path only works for installed file URLs.
675 GURL gurl(file_url);
676 if (!gurl.SchemeIs("chrome-extension"))
677 return PP_kInvalidFileHandle;
679 content::PepperPluginInstance* plugin_instance =
680 content::PepperPluginInstance::Get(instance);
681 if (!plugin_instance)
682 return PP_kInvalidFileHandle;
683 // IMPORTANT: Make sure the document can request the given URL. If we don't
684 // check, a malicious app could probe the extension system. This enforces a
685 // same-origin policy which prevents the app from requesting resources from
686 // another app.
687 blink::WebSecurityOrigin security_origin =
688 plugin_instance->GetContainer()->element().document().securityOrigin();
689 if (!security_origin.canRequest(gurl))
690 return PP_kInvalidFileHandle;
692 IPC::PlatformFileForTransit out_fd = IPC::InvalidPlatformFileForTransit();
693 IPC::Sender* sender = content::RenderThread::Get();
694 DCHECK(sender);
695 *nonce_lo = 0;
696 *nonce_hi = 0;
697 base::FilePath file_path;
698 if (!sender->Send(
699 new NaClHostMsg_OpenNaClExecutable(GetRoutingID(instance),
700 GURL(file_url),
701 &out_fd,
702 nonce_lo,
703 nonce_hi))) {
704 return PP_kInvalidFileHandle;
707 if (out_fd == IPC::InvalidPlatformFileForTransit())
708 return PP_kInvalidFileHandle;
710 return IPC::PlatformFileForTransitToPlatformFile(out_fd);
713 void DispatchEvent(PP_Instance instance,
714 PP_NaClEventType event_type,
715 const char *resource_url,
716 PP_Bool length_is_computable,
717 uint64_t loaded_bytes,
718 uint64_t total_bytes) {
719 ProgressEvent event(event_type,
720 resource_url,
721 PP_ToBool(length_is_computable),
722 loaded_bytes,
723 total_bytes);
724 DispatchProgressEvent(instance, event);
727 void ReportLoadSuccess(PP_Instance instance,
728 uint64_t loaded_bytes,
729 uint64_t total_bytes) {
730 NexeLoadManager* load_manager = NexeLoadManager::Get(instance);
731 if (load_manager) {
732 load_manager->ReportLoadSuccess(load_manager->program_url(),
733 loaded_bytes,
734 total_bytes);
738 void ReportLoadError(PP_Instance instance,
739 PP_NaClError error,
740 const char* error_message) {
741 NexeLoadManager* load_manager = NexeLoadManager::Get(instance);
742 if (load_manager)
743 load_manager->ReportLoadError(error, error_message);
746 void ReportLoadAbort(PP_Instance instance) {
747 NexeLoadManager* load_manager = NexeLoadManager::Get(instance);
748 if (load_manager)
749 load_manager->ReportLoadAbort();
752 void NexeDidCrash(PP_Instance instance) {
753 NexeLoadManager* load_manager = NexeLoadManager::Get(instance);
754 if (load_manager)
755 load_manager->NexeDidCrash();
758 void InstanceCreated(PP_Instance instance) {
759 NexeLoadManager::Create(instance);
762 void InstanceDestroyed(PP_Instance instance) {
763 DeleteJsonManifest(instance);
764 NexeLoadManager::Delete(instance);
767 PP_Bool NaClDebugEnabledForURL(const char* alleged_nmf_url) {
768 if (!CommandLine::ForCurrentProcess()->HasSwitch(switches::kEnableNaClDebug))
769 return PP_FALSE;
770 IPC::Sender* sender = content::RenderThread::Get();
771 DCHECK(sender);
772 bool should_debug = false;
773 return PP_FromBool(
774 sender->Send(new NaClHostMsg_NaClDebugEnabledForURL(GURL(alleged_nmf_url),
775 &should_debug)) &&
776 should_debug);
779 void LogToConsole(PP_Instance instance, const char* message) {
780 NexeLoadManager* load_manager = NexeLoadManager::Get(instance);
781 DCHECK(load_manager);
782 if (load_manager)
783 load_manager->LogToConsole(std::string(message));
786 PP_NaClReadyState GetNaClReadyState(PP_Instance instance) {
787 NexeLoadManager* load_manager = NexeLoadManager::Get(instance);
788 DCHECK(load_manager);
789 if (load_manager)
790 return load_manager->nacl_ready_state();
791 return PP_NACL_READY_STATE_UNSENT;
794 void Vlog(const char* message) {
795 VLOG(1) << message;
798 void InitializePlugin(PP_Instance instance,
799 uint32_t argc,
800 const char* argn[],
801 const char* argv[]) {
802 NexeLoadManager* load_manager = NexeLoadManager::Get(instance);
803 DCHECK(load_manager);
804 if (load_manager)
805 load_manager->InitializePlugin(argc, argn, argv);
808 int64_t GetNexeSize(PP_Instance instance) {
809 NexeLoadManager* load_manager = NexeLoadManager::Get(instance);
810 DCHECK(load_manager);
811 if (load_manager)
812 return load_manager->nexe_size();
813 return 0;
816 void DownloadManifestToBuffer(PP_Instance instance,
817 struct PP_CompletionCallback callback);
819 bool CreateJsonManifest(PP_Instance instance,
820 const std::string& manifest_url,
821 const std::string& manifest_data);
823 void RequestNaClManifest(PP_Instance instance,
824 PP_CompletionCallback callback) {
825 NexeLoadManager* load_manager = NexeLoadManager::Get(instance);
826 DCHECK(load_manager);
827 if (!load_manager) {
828 ppapi::PpapiGlobals::Get()->GetMainThreadMessageLoop()->PostTask(
829 FROM_HERE,
830 base::Bind(callback.func, callback.user_data,
831 static_cast<int32_t>(PP_ERROR_FAILED)));
832 return;
835 std::string url = load_manager->GetManifestURLArgument();
836 if (url.empty() || !load_manager->RequestNaClManifest(url)) {
837 ppapi::PpapiGlobals::Get()->GetMainThreadMessageLoop()->PostTask(
838 FROM_HERE,
839 base::Bind(callback.func, callback.user_data,
840 static_cast<int32_t>(PP_ERROR_FAILED)));
841 return;
844 const GURL& base_url = load_manager->manifest_base_url();
845 if (base_url.SchemeIs("data")) {
846 GURL gurl(base_url);
847 std::string mime_type;
848 std::string charset;
849 std::string data;
850 int32_t error = PP_ERROR_FAILED;
851 if (net::DataURL::Parse(gurl, &mime_type, &charset, &data)) {
852 if (data.size() <= ManifestDownloader::kNaClManifestMaxFileBytes) {
853 if (CreateJsonManifest(instance, base_url.spec(), data))
854 error = PP_OK;
855 } else {
856 load_manager->ReportLoadError(PP_NACL_ERROR_MANIFEST_TOO_LARGE,
857 "manifest file too large.");
859 } else {
860 load_manager->ReportLoadError(PP_NACL_ERROR_MANIFEST_LOAD_URL,
861 "could not load manifest url.");
863 ppapi::PpapiGlobals::Get()->GetMainThreadMessageLoop()->PostTask(
864 FROM_HERE,
865 base::Bind(callback.func, callback.user_data, error));
866 } else {
867 DownloadManifestToBuffer(instance, callback);
871 PP_Var GetManifestBaseURL(PP_Instance instance) {
872 NexeLoadManager* load_manager = NexeLoadManager::Get(instance);
873 DCHECK(load_manager);
874 if (!load_manager)
875 return PP_MakeUndefined();
876 const GURL& gurl = load_manager->manifest_base_url();
877 if (!gurl.is_valid())
878 return PP_MakeUndefined();
879 return ppapi::StringVar::StringToPPVar(gurl.spec());
882 void ProcessNaClManifest(PP_Instance instance, const char* program_url) {
883 nacl::NexeLoadManager* load_manager = NexeLoadManager::Get(instance);
884 if (load_manager)
885 load_manager->ProcessNaClManifest(program_url);
888 PP_Bool DevInterfacesEnabled(PP_Instance instance) {
889 nacl::NexeLoadManager* load_manager = NexeLoadManager::Get(instance);
890 if (load_manager)
891 return PP_FromBool(load_manager->DevInterfacesEnabled());
892 return PP_FALSE;
895 void DownloadManifestToBufferCompletion(PP_Instance instance,
896 struct PP_CompletionCallback callback,
897 base::Time start_time,
898 PP_NaClError pp_nacl_error,
899 const std::string& data);
901 void DownloadManifestToBuffer(PP_Instance instance,
902 struct PP_CompletionCallback callback) {
903 nacl::NexeLoadManager* load_manager = NexeLoadManager::Get(instance);
904 DCHECK(load_manager);
905 content::PepperPluginInstance* plugin_instance =
906 content::PepperPluginInstance::Get(instance);
907 if (!load_manager || !plugin_instance) {
908 ppapi::PpapiGlobals::Get()->GetMainThreadMessageLoop()->PostTask(
909 FROM_HERE,
910 base::Bind(callback.func, callback.user_data,
911 static_cast<int32_t>(PP_ERROR_FAILED)));
913 const blink::WebDocument& document =
914 plugin_instance->GetContainer()->element().document();
916 const GURL& gurl = load_manager->manifest_base_url();
917 scoped_ptr<blink::WebURLLoader> url_loader(
918 CreateWebURLLoader(document, gurl));
919 blink::WebURLRequest request = CreateWebURLRequest(document, gurl);
921 // ManifestDownloader deletes itself after invoking the callback.
922 ManifestDownloader* manifest_downloader = new ManifestDownloader(
923 url_loader.Pass(),
924 load_manager->is_installed(),
925 base::Bind(DownloadManifestToBufferCompletion,
926 instance, callback, base::Time::Now()));
927 manifest_downloader->Load(request);
930 void DownloadManifestToBufferCompletion(PP_Instance instance,
931 struct PP_CompletionCallback callback,
932 base::Time start_time,
933 PP_NaClError pp_nacl_error,
934 const std::string& data) {
935 base::TimeDelta download_time = base::Time::Now() - start_time;
936 HistogramTimeSmall("NaCl.Perf.StartupTime.ManifestDownload",
937 download_time.InMilliseconds());
939 nacl::NexeLoadManager* load_manager = NexeLoadManager::Get(instance);
940 if (!load_manager) {
941 callback.func(callback.user_data, PP_ERROR_ABORTED);
942 return;
945 int32_t pp_error;
946 switch (pp_nacl_error) {
947 case PP_NACL_ERROR_LOAD_SUCCESS:
948 pp_error = PP_OK;
949 break;
950 case PP_NACL_ERROR_MANIFEST_LOAD_URL:
951 pp_error = PP_ERROR_FAILED;
952 load_manager->ReportLoadError(PP_NACL_ERROR_MANIFEST_LOAD_URL,
953 "could not load manifest url.");
954 break;
955 case PP_NACL_ERROR_MANIFEST_TOO_LARGE:
956 pp_error = PP_ERROR_FILETOOBIG;
957 load_manager->ReportLoadError(PP_NACL_ERROR_MANIFEST_TOO_LARGE,
958 "manifest file too large.");
959 break;
960 case PP_NACL_ERROR_MANIFEST_NOACCESS_URL:
961 pp_error = PP_ERROR_NOACCESS;
962 load_manager->ReportLoadError(PP_NACL_ERROR_MANIFEST_NOACCESS_URL,
963 "access to manifest url was denied.");
964 break;
965 default:
966 NOTREACHED();
967 pp_error = PP_ERROR_FAILED;
968 load_manager->ReportLoadError(PP_NACL_ERROR_MANIFEST_LOAD_URL,
969 "could not load manifest url.");
972 if (pp_error == PP_OK) {
973 std::string base_url = load_manager->manifest_base_url().spec();
974 if (!CreateJsonManifest(instance, base_url, data))
975 pp_error = PP_ERROR_FAILED;
977 callback.func(callback.user_data, pp_error);
980 bool CreateJsonManifest(PP_Instance instance,
981 const std::string& manifest_url,
982 const std::string& manifest_data) {
983 HistogramSizeKB("NaCl.Perf.Size.Manifest",
984 static_cast<int32_t>(manifest_data.length() / 1024));
986 nacl::NexeLoadManager* load_manager = NexeLoadManager::Get(instance);
987 if (!load_manager)
988 return false;
990 const char* isa_type;
991 if (load_manager->IsPNaCl())
992 isa_type = kPortableArch;
993 else
994 isa_type = GetSandboxArch();
996 scoped_ptr<nacl::JsonManifest> j(
997 new nacl::JsonManifest(
998 manifest_url.c_str(),
999 isa_type,
1000 IsNonSFIModeEnabled(),
1001 PP_ToBool(NaClDebugEnabledForURL(manifest_url.c_str()))));
1002 JsonManifest::ErrorInfo error_info;
1003 if (j->Init(manifest_data.c_str(), &error_info)) {
1004 AddJsonManifest(instance, j.Pass());
1005 return true;
1007 load_manager->ReportLoadError(error_info.error, error_info.string);
1008 return false;
1011 PP_Bool ManifestGetProgramURL(PP_Instance instance,
1012 PP_Var* pp_full_url,
1013 PP_PNaClOptions* pnacl_options,
1014 PP_Bool* pp_uses_nonsfi_mode) {
1015 nacl::NexeLoadManager* load_manager = NexeLoadManager::Get(instance);
1017 JsonManifest* manifest = GetJsonManifest(instance);
1018 if (manifest == NULL)
1019 return PP_FALSE;
1021 bool uses_nonsfi_mode;
1022 std::string full_url;
1023 JsonManifest::ErrorInfo error_info;
1024 if (manifest->GetProgramURL(&full_url, pnacl_options, &uses_nonsfi_mode,
1025 &error_info)) {
1026 *pp_full_url = ppapi::StringVar::StringToPPVar(full_url);
1027 *pp_uses_nonsfi_mode = PP_FromBool(uses_nonsfi_mode);
1028 return PP_TRUE;
1031 if (load_manager)
1032 load_manager->ReportLoadError(error_info.error, error_info.string);
1033 return PP_FALSE;
1036 bool ManifestResolveKey(PP_Instance instance,
1037 bool is_helper_process,
1038 const std::string& key,
1039 std::string* full_url,
1040 PP_PNaClOptions* pnacl_options) {
1041 // For "helper" processes (llc and ld, for PNaCl translation), we resolve
1042 // keys manually as there is no existing .nmf file to parse.
1043 if (is_helper_process) {
1044 pnacl_options->translate = PP_FALSE;
1045 // We can only resolve keys in the files/ namespace.
1046 const std::string kFilesPrefix = "files/";
1047 if (key.find(kFilesPrefix) == std::string::npos) {
1048 nacl::NexeLoadManager* load_manager = NexeLoadManager::Get(instance);
1049 if (load_manager)
1050 load_manager->ReportLoadError(PP_NACL_ERROR_MANIFEST_RESOLVE_URL,
1051 "key did not start with files/");
1052 return false;
1054 std::string key_basename = key.substr(kFilesPrefix.length());
1055 *full_url = std::string(kPNaClTranslatorBaseUrl) + GetSandboxArch() + "/" +
1056 key_basename;
1057 return true;
1060 JsonManifest* manifest = GetJsonManifest(instance);
1061 if (manifest == NULL)
1062 return false;
1064 return manifest->ResolveKey(key, full_url, pnacl_options);
1067 PP_Bool GetPNaClResourceInfo(PP_Instance instance,
1068 PP_Var* llc_tool_name,
1069 PP_Var* ld_tool_name) {
1070 static const char kFilename[] = "chrome://pnacl-translator/pnacl.json";
1071 NexeLoadManager* load_manager = NexeLoadManager::Get(instance);
1072 DCHECK(load_manager);
1073 if (!load_manager)
1074 return PP_FALSE;
1076 uint64_t nonce_lo = 0;
1077 uint64_t nonce_hi = 0;
1078 base::File file(GetReadonlyPnaclFd(kFilename, false /* is_executable */,
1079 &nonce_lo, &nonce_hi));
1080 if (!file.IsValid()) {
1081 load_manager->ReportLoadError(
1082 PP_NACL_ERROR_PNACL_RESOURCE_FETCH,
1083 "The Portable Native Client (pnacl) component is not "
1084 "installed. Please consult chrome://components for more "
1085 "information.");
1086 return PP_FALSE;
1089 base::File::Info file_info;
1090 if (!file.GetInfo(&file_info)) {
1091 load_manager->ReportLoadError(
1092 PP_NACL_ERROR_PNACL_RESOURCE_FETCH,
1093 std::string("GetPNaClResourceInfo, GetFileInfo failed for: ") +
1094 kFilename);
1095 return PP_FALSE;
1098 if (file_info.size > 1 << 20) {
1099 load_manager->ReportLoadError(
1100 PP_NACL_ERROR_PNACL_RESOURCE_FETCH,
1101 std::string("GetPNaClResourceInfo, file too large: ") + kFilename);
1102 return PP_FALSE;
1105 scoped_ptr<char[]> buffer(new char[file_info.size + 1]);
1106 if (buffer.get() == NULL) {
1107 load_manager->ReportLoadError(
1108 PP_NACL_ERROR_PNACL_RESOURCE_FETCH,
1109 std::string("GetPNaClResourceInfo, couldn't allocate for: ") +
1110 kFilename);
1111 return PP_FALSE;
1114 int rc = file.Read(0, buffer.get(), file_info.size);
1115 if (rc < 0) {
1116 load_manager->ReportLoadError(
1117 PP_NACL_ERROR_PNACL_RESOURCE_FETCH,
1118 std::string("GetPNaClResourceInfo, reading failed for: ") + kFilename);
1119 return PP_FALSE;
1122 // Null-terminate the bytes we we read from the file.
1123 buffer.get()[rc] = 0;
1125 // Expect the JSON file to contain a top-level object (dictionary).
1126 Json::Reader json_reader;
1127 Json::Value json_data;
1128 if (!json_reader.parse(buffer.get(), json_data)) {
1129 load_manager->ReportLoadError(
1130 PP_NACL_ERROR_PNACL_RESOURCE_FETCH,
1131 std::string("Parsing resource info failed: JSON parse error: ") +
1132 json_reader.getFormattedErrorMessages());
1133 return PP_FALSE;
1136 if (!json_data.isObject()) {
1137 load_manager->ReportLoadError(
1138 PP_NACL_ERROR_PNACL_RESOURCE_FETCH,
1139 "Parsing resource info failed: Malformed JSON dictionary");
1140 return PP_FALSE;
1143 if (json_data.isMember("pnacl-llc-name")) {
1144 Json::Value json_name = json_data["pnacl-llc-name"];
1145 if (json_name.isString()) {
1146 std::string llc_tool_name_str = json_name.asString();
1147 *llc_tool_name = ppapi::StringVar::StringToPPVar(llc_tool_name_str);
1151 if (json_data.isMember("pnacl-ld-name")) {
1152 Json::Value json_name = json_data["pnacl-ld-name"];
1153 if (json_name.isString()) {
1154 std::string ld_tool_name_str = json_name.asString();
1155 *ld_tool_name = ppapi::StringVar::StringToPPVar(ld_tool_name_str);
1158 return PP_TRUE;
1161 PP_Var GetCpuFeatureAttrs() {
1162 return ppapi::StringVar::StringToPPVar(GetCpuFeatures());
1165 void PostMessageToJavaScriptMainThread(PP_Instance instance,
1166 const std::string& message) {
1167 content::PepperPluginInstance* plugin_instance =
1168 content::PepperPluginInstance::Get(instance);
1169 if (plugin_instance) {
1170 PP_Var message_var = ppapi::StringVar::StringToPPVar(message);
1171 plugin_instance->PostMessageToJavaScript(message_var);
1172 ppapi::PpapiGlobals::Get()->GetVarTracker()->ReleaseVar(message_var);
1176 void PostMessageToJavaScript(PP_Instance instance, const char* message) {
1177 ppapi::PpapiGlobals::Get()->GetMainThreadMessageLoop()->PostTask(
1178 FROM_HERE,
1179 base::Bind(&PostMessageToJavaScriptMainThread,
1180 instance,
1181 std::string(message)));
1184 // Encapsulates some of the state for a call to DownloadNexe to prevent
1185 // argument lists from getting too long.
1186 struct DownloadNexeRequest {
1187 PP_Instance instance;
1188 std::string url;
1189 PP_CompletionCallback callback;
1190 base::Time start_time;
1193 // A utility class to ensure that we don't send progress events more often than
1194 // every 10ms for a given file.
1195 class ProgressEventRateLimiter {
1196 public:
1197 explicit ProgressEventRateLimiter(PP_Instance instance)
1198 : instance_(instance) { }
1200 void ReportProgress(const std::string& url,
1201 int64_t total_bytes_received,
1202 int64_t total_bytes_to_be_received) {
1203 base::Time now = base::Time::Now();
1204 if (now - last_event_ > base::TimeDelta::FromMilliseconds(10)) {
1205 DispatchProgressEvent(instance_,
1206 ProgressEvent(PP_NACL_EVENT_PROGRESS,
1207 url,
1208 total_bytes_to_be_received >= 0,
1209 total_bytes_received,
1210 total_bytes_to_be_received));
1211 last_event_ = now;
1215 private:
1216 PP_Instance instance_;
1217 base::Time last_event_;
1220 void DownloadNexeCompletion(const DownloadNexeRequest& request,
1221 PP_NaClFileInfo* out_file_info,
1222 FileDownloader::Status status,
1223 base::File target_file,
1224 int http_status);
1226 void DownloadNexe(PP_Instance instance,
1227 const char* url,
1228 PP_NaClFileInfo* out_file_info,
1229 PP_CompletionCallback callback) {
1230 CHECK(url);
1231 CHECK(out_file_info);
1232 DownloadNexeRequest request;
1233 request.instance = instance;
1234 request.url = url;
1235 request.callback = callback;
1236 request.start_time = base::Time::Now();
1238 // Try the fast path for retrieving the file first.
1239 PP_FileHandle handle = OpenNaClExecutable(instance,
1240 url,
1241 &out_file_info->token_lo,
1242 &out_file_info->token_hi);
1243 if (handle != PP_kInvalidFileHandle) {
1244 DownloadNexeCompletion(request,
1245 out_file_info,
1246 FileDownloader::SUCCESS,
1247 base::File(handle),
1248 200);
1249 return;
1252 // The fast path didn't work, we'll fetch the file using URLLoader and write
1253 // it to local storage.
1254 base::File target_file(CreateTemporaryFile(instance));
1255 GURL gurl(url);
1257 content::PepperPluginInstance* plugin_instance =
1258 content::PepperPluginInstance::Get(instance);
1259 if (!plugin_instance) {
1260 ppapi::PpapiGlobals::Get()->GetMainThreadMessageLoop()->PostTask(
1261 FROM_HERE,
1262 base::Bind(callback.func, callback.user_data,
1263 static_cast<int32_t>(PP_ERROR_FAILED)));
1265 const blink::WebDocument& document =
1266 plugin_instance->GetContainer()->element().document();
1267 scoped_ptr<blink::WebURLLoader> url_loader(
1268 CreateWebURLLoader(document, gurl));
1269 blink::WebURLRequest url_request = CreateWebURLRequest(document, gurl);
1271 ProgressEventRateLimiter* tracker = new ProgressEventRateLimiter(instance);
1273 // FileDownloader deletes itself after invoking DownloadNexeCompletion.
1274 FileDownloader* file_downloader = new FileDownloader(
1275 url_loader.Pass(),
1276 target_file.Pass(),
1277 base::Bind(&DownloadNexeCompletion, request, out_file_info),
1278 base::Bind(&ProgressEventRateLimiter::ReportProgress,
1279 base::Owned(tracker), std::string(url)));
1280 file_downloader->Load(url_request);
1283 void DownloadNexeCompletion(const DownloadNexeRequest& request,
1284 PP_NaClFileInfo* out_file_info,
1285 FileDownloader::Status status,
1286 base::File target_file,
1287 int http_status) {
1288 int32_t pp_error = FileDownloaderToPepperError(status);
1289 int64_t bytes_read = -1;
1290 if (pp_error == PP_OK && target_file.IsValid()) {
1291 base::File::Info info;
1292 if (target_file.GetInfo(&info))
1293 bytes_read = info.size;
1296 if (bytes_read == -1) {
1297 target_file.Close();
1298 pp_error = PP_ERROR_FAILED;
1301 base::TimeDelta download_time = base::Time::Now() - request.start_time;
1303 NexeLoadManager* load_manager = NexeLoadManager::Get(request.instance);
1304 if (load_manager) {
1305 load_manager->NexeFileDidOpen(pp_error,
1306 target_file,
1307 http_status,
1308 bytes_read,
1309 request.url,
1310 download_time);
1313 if (pp_error == PP_OK && target_file.IsValid())
1314 out_file_info->handle = target_file.TakePlatformFile();
1315 else
1316 out_file_info->handle = PP_kInvalidFileHandle;
1318 request.callback.func(request.callback.user_data, pp_error);
1321 void DownloadFileCompletion(
1322 const DownloadFileCallback& callback,
1323 FileDownloader::Status status,
1324 base::File file,
1325 int http_status) {
1326 int32_t pp_error = FileDownloaderToPepperError(status);
1327 PP_NaClFileInfo file_info;
1328 if (pp_error == PP_OK) {
1329 file_info.handle = file.TakePlatformFile();
1330 file_info.token_lo = 0;
1331 file_info.token_hi = 0;
1332 } else {
1333 file_info = kInvalidNaClFileInfo;
1336 callback.Run(pp_error, file_info);
1339 void DownloadFile(PP_Instance instance,
1340 const std::string& url,
1341 const DownloadFileCallback& callback) {
1342 DCHECK(ppapi::PpapiGlobals::Get()->GetMainThreadMessageLoop()->
1343 BelongsToCurrentThread());
1345 NexeLoadManager* load_manager = NexeLoadManager::Get(instance);
1346 DCHECK(load_manager);
1347 if (!load_manager) {
1348 base::MessageLoop::current()->PostTask(
1349 FROM_HERE,
1350 base::Bind(callback,
1351 static_cast<int32_t>(PP_ERROR_FAILED),
1352 kInvalidNaClFileInfo));
1353 return;
1356 // Handle special PNaCl support files which are installed on the user's
1357 // machine.
1358 if (url.find(kPNaClTranslatorBaseUrl, 0) == 0) {
1359 PP_NaClFileInfo file_info = kInvalidNaClFileInfo;
1360 PP_FileHandle handle = GetReadonlyPnaclFd(url.c_str(),
1361 false /* is_executable */,
1362 &file_info.token_lo,
1363 &file_info.token_hi);
1364 if (handle == PP_kInvalidFileHandle) {
1365 base::MessageLoop::current()->PostTask(
1366 FROM_HERE,
1367 base::Bind(callback,
1368 static_cast<int32_t>(PP_ERROR_FAILED),
1369 kInvalidNaClFileInfo));
1370 return;
1372 file_info.handle = handle;
1373 base::MessageLoop::current()->PostTask(
1374 FROM_HERE,
1375 base::Bind(callback, static_cast<int32_t>(PP_OK), file_info));
1376 return;
1379 // We have to ensure that this url resolves relative to the plugin base url
1380 // before downloading it.
1381 const GURL& test_gurl = load_manager->plugin_base_url().Resolve(url);
1382 if (!test_gurl.is_valid()) {
1383 base::MessageLoop::current()->PostTask(
1384 FROM_HERE,
1385 base::Bind(callback,
1386 static_cast<int32_t>(PP_ERROR_FAILED),
1387 kInvalidNaClFileInfo));
1388 return;
1391 // Try the fast path for retrieving the file first.
1392 uint64_t file_token_lo = 0;
1393 uint64_t file_token_hi = 0;
1394 PP_FileHandle file_handle = OpenNaClExecutable(instance,
1395 url.c_str(),
1396 &file_token_lo,
1397 &file_token_hi);
1398 if (file_handle != PP_kInvalidFileHandle) {
1399 PP_NaClFileInfo file_info;
1400 file_info.handle = file_handle;
1401 file_info.token_lo = file_token_lo;
1402 file_info.token_hi = file_token_hi;
1403 base::MessageLoop::current()->PostTask(
1404 FROM_HERE,
1405 base::Bind(callback, static_cast<int32_t>(PP_OK), file_info));
1406 return;
1409 // The fast path didn't work, we'll fetch the file using URLLoader and write
1410 // it to local storage.
1411 base::File target_file(CreateTemporaryFile(instance));
1412 GURL gurl(url);
1414 content::PepperPluginInstance* plugin_instance =
1415 content::PepperPluginInstance::Get(instance);
1416 if (!plugin_instance) {
1417 base::MessageLoop::current()->PostTask(
1418 FROM_HERE,
1419 base::Bind(callback,
1420 static_cast<int32_t>(PP_ERROR_FAILED),
1421 kInvalidNaClFileInfo));
1423 const blink::WebDocument& document =
1424 plugin_instance->GetContainer()->element().document();
1425 scoped_ptr<blink::WebURLLoader> url_loader(
1426 CreateWebURLLoader(document, gurl));
1427 blink::WebURLRequest url_request = CreateWebURLRequest(document, gurl);
1429 ProgressEventRateLimiter* tracker = new ProgressEventRateLimiter(instance);
1431 // FileDownloader deletes itself after invoking DownloadNexeCompletion.
1432 FileDownloader* file_downloader = new FileDownloader(
1433 url_loader.Pass(),
1434 target_file.Pass(),
1435 base::Bind(&DownloadFileCompletion, callback),
1436 base::Bind(&ProgressEventRateLimiter::ReportProgress,
1437 base::Owned(tracker), std::string(url)));
1438 file_downloader->Load(url_request);
1441 void ReportSelLdrStatus(PP_Instance instance,
1442 int32_t load_status,
1443 int32_t max_status) {
1444 HistogramEnumerate("NaCl.LoadStatus.SelLdr", load_status, max_status);
1445 NexeLoadManager* load_manager = NexeLoadManager::Get(instance);
1446 DCHECK(load_manager);
1447 if (!load_manager)
1448 return;
1450 // Gather data to see if being installed changes load outcomes.
1451 const char* name = load_manager->is_installed() ?
1452 "NaCl.LoadStatus.SelLdr.InstalledApp" :
1453 "NaCl.LoadStatus.SelLdr.NotInstalledApp";
1454 HistogramEnumerate(name, load_status, max_status);
1457 void LogTranslateTime(const char* histogram_name,
1458 int64_t time_in_us) {
1459 ppapi::PpapiGlobals::Get()->GetMainThreadMessageLoop()->PostTask(
1460 FROM_HERE,
1461 base::Bind(&HistogramTimeTranslation,
1462 std::string(histogram_name),
1463 time_in_us / 1000));
1466 void DidOpenManifestEntry(PP_NaClFileInfo* out_file_info,
1467 PP_CompletionCallback callback,
1468 int32_t pp_error,
1469 const PP_NaClFileInfo& file_info) {
1470 if (pp_error == PP_OK)
1471 *out_file_info = file_info;
1472 callback.func(callback.user_data, pp_error);
1475 void OpenManifestEntry(PP_Instance instance,
1476 PP_Bool is_helper_process,
1477 const char* key,
1478 PP_NaClFileInfo* out_file_info,
1479 PP_CompletionCallback callback) {
1480 std::string url;
1481 PP_PNaClOptions pnacl_options;
1482 pnacl_options.translate = PP_FALSE;
1483 pnacl_options.is_debug = PP_FALSE;
1484 pnacl_options.opt_level = 2;
1485 if (!ManifestResolveKey(instance,
1486 PP_ToBool(is_helper_process),
1487 key,
1488 &url,
1489 &pnacl_options)) {
1490 PostPPCompletionCallback(callback, PP_ERROR_FAILED);
1493 // TODO(teravest): Make a type like PP_NaClFileInfo to use for DownloadFile
1494 // that would close the file handle on destruction.
1495 DownloadFile(instance, url,
1496 base::Bind(&DidOpenManifestEntry, out_file_info, callback));
1499 void SetPNaClStartTime(PP_Instance instance) {
1500 NexeLoadManager* load_manager = NexeLoadManager::Get(instance);
1501 if (load_manager)
1502 load_manager->set_pnacl_start_time(base::Time::Now());
1505 // PexeDownloader is responsible for deleting itself when the download
1506 // finishes.
1507 class PexeDownloader : public blink::WebURLLoaderClient {
1508 public:
1509 PexeDownloader(PP_Instance instance,
1510 scoped_ptr<blink::WebURLLoader> url_loader,
1511 const std::string& pexe_url,
1512 int32_t pexe_opt_level,
1513 const PPP_PexeStreamHandler* stream_handler,
1514 void* stream_handler_user_data)
1515 : instance_(instance),
1516 url_loader_(url_loader.Pass()),
1517 pexe_url_(pexe_url),
1518 pexe_opt_level_(pexe_opt_level),
1519 stream_handler_(stream_handler),
1520 stream_handler_user_data_(stream_handler_user_data),
1521 success_(false),
1522 expected_content_length_(-1),
1523 weak_factory_(this) { }
1525 void Load(const blink::WebURLRequest& request) {
1526 url_loader_->loadAsynchronously(request, this);
1529 private:
1530 virtual void didReceiveResponse(blink::WebURLLoader* loader,
1531 const blink::WebURLResponse& response) {
1532 success_ = (response.httpStatusCode() == 200);
1533 if (!success_)
1534 return;
1536 expected_content_length_ = response.expectedContentLength();
1538 // Defer loading after receiving headers. This is because we may already
1539 // have a cached translated nexe, so check for that now.
1540 url_loader_->setDefersLoading(true);
1542 std::string etag = response.httpHeaderField("etag").utf8();
1543 std::string last_modified =
1544 response.httpHeaderField("last-modified").utf8();
1545 base::Time last_modified_time;
1546 base::Time::FromString(last_modified.c_str(), &last_modified_time);
1548 bool has_no_store_header = false;
1549 std::string cache_control =
1550 response.httpHeaderField("cache-control").utf8();
1552 std::vector<std::string> values;
1553 base::SplitString(cache_control, ',', &values);
1554 for (std::vector<std::string>::const_iterator it = values.begin();
1555 it != values.end();
1556 ++it) {
1557 if (base::StringToLowerASCII(*it) == "no-store")
1558 has_no_store_header = true;
1561 GetNexeFd(instance_,
1562 pexe_url_,
1563 pexe_opt_level_,
1564 last_modified_time,
1565 etag,
1566 has_no_store_header,
1567 base::Bind(&PexeDownloader::didGetNexeFd,
1568 weak_factory_.GetWeakPtr()));
1571 virtual void didGetNexeFd(int32_t pp_error,
1572 bool cache_hit,
1573 PP_FileHandle file_handle) {
1574 if (!content::PepperPluginInstance::Get(instance_)) {
1575 delete this;
1576 return;
1579 HistogramEnumerate("NaCl.Perf.PNaClCache.IsHit", cache_hit, 2);
1580 if (cache_hit) {
1581 stream_handler_->DidCacheHit(stream_handler_user_data_, file_handle);
1583 // We delete the PexeDownloader at this point since we successfully got a
1584 // cached, translated nexe.
1585 delete this;
1586 return;
1588 stream_handler_->DidCacheMiss(stream_handler_user_data_,
1589 expected_content_length_,
1590 file_handle);
1592 // No translated nexe was found in the cache, so we should download the
1593 // file to start streaming it.
1594 url_loader_->setDefersLoading(false);
1597 virtual void didReceiveData(blink::WebURLLoader* loader,
1598 const char* data,
1599 int data_length,
1600 int encoded_data_length) {
1601 if (content::PepperPluginInstance::Get(instance_)) {
1602 // Stream the data we received to the stream callback.
1603 stream_handler_->DidStreamData(stream_handler_user_data_,
1604 data,
1605 data_length);
1609 virtual void didFinishLoading(blink::WebURLLoader* loader,
1610 double finish_time,
1611 int64_t total_encoded_data_length) {
1612 int32_t result = success_ ? PP_OK : PP_ERROR_FAILED;
1614 if (content::PepperPluginInstance::Get(instance_))
1615 stream_handler_->DidFinishStream(stream_handler_user_data_, result);
1616 delete this;
1619 virtual void didFail(blink::WebURLLoader* loader,
1620 const blink::WebURLError& error) {
1621 success_ = false;
1624 PP_Instance instance_;
1625 scoped_ptr<blink::WebURLLoader> url_loader_;
1626 std::string pexe_url_;
1627 int32_t pexe_opt_level_;
1628 const PPP_PexeStreamHandler* stream_handler_;
1629 void* stream_handler_user_data_;
1630 bool success_;
1631 int64_t expected_content_length_;
1632 base::WeakPtrFactory<PexeDownloader> weak_factory_;
1635 void StreamPexe(PP_Instance instance,
1636 const char* pexe_url,
1637 int32_t opt_level,
1638 const PPP_PexeStreamHandler* handler,
1639 void* handler_user_data) {
1640 content::PepperPluginInstance* plugin_instance =
1641 content::PepperPluginInstance::Get(instance);
1642 if (!plugin_instance) {
1643 base::MessageLoop::current()->PostTask(
1644 FROM_HERE,
1645 base::Bind(handler->DidFinishStream,
1646 handler_user_data,
1647 static_cast<int32_t>(PP_ERROR_FAILED)));
1648 return;
1651 GURL gurl(pexe_url);
1652 const blink::WebDocument& document =
1653 plugin_instance->GetContainer()->element().document();
1654 scoped_ptr<blink::WebURLLoader> url_loader(
1655 CreateWebURLLoader(document, gurl));
1656 PexeDownloader* downloader = new PexeDownloader(instance,
1657 url_loader.Pass(),
1658 pexe_url,
1659 opt_level,
1660 handler,
1661 handler_user_data);
1663 blink::WebURLRequest url_request = CreateWebURLRequest(document, gurl);
1664 // Mark the request as requesting a PNaCl bitcode file,
1665 // so that component updater can detect this user action.
1666 url_request.addHTTPHeaderField(
1667 blink::WebString::fromUTF8("Accept"),
1668 blink::WebString::fromUTF8("application/x-pnacl, */*"));
1669 downloader->Load(url_request);
1672 const PPB_NaCl_Private nacl_interface = {
1673 &LaunchSelLdr,
1674 &StartPpapiProxy,
1675 &UrandomFD,
1676 &Are3DInterfacesDisabled,
1677 &BrokerDuplicateHandle,
1678 &GetReadExecPnaclFd,
1679 &CreateTemporaryFile,
1680 &GetNumberOfProcessors,
1681 &PPIsNonSFIModeEnabled,
1682 &ReportTranslationFinished,
1683 &DispatchEvent,
1684 &ReportLoadSuccess,
1685 &ReportLoadError,
1686 &ReportLoadAbort,
1687 &NexeDidCrash,
1688 &InstanceCreated,
1689 &InstanceDestroyed,
1690 &NaClDebugEnabledForURL,
1691 &GetSandboxArch,
1692 &LogToConsole,
1693 &GetNaClReadyState,
1694 &Vlog,
1695 &InitializePlugin,
1696 &GetNexeSize,
1697 &RequestNaClManifest,
1698 &GetManifestBaseURL,
1699 &ProcessNaClManifest,
1700 &DevInterfacesEnabled,
1701 &ManifestGetProgramURL,
1702 &GetPNaClResourceInfo,
1703 &GetCpuFeatureAttrs,
1704 &PostMessageToJavaScript,
1705 &DownloadNexe,
1706 &ReportSelLdrStatus,
1707 &LogTranslateTime,
1708 &OpenManifestEntry,
1709 &SetPNaClStartTime,
1710 &StreamPexe
1713 } // namespace
1715 const PPB_NaCl_Private* GetNaClPrivateInterface() {
1716 return &nacl_interface;
1719 } // namespace nacl