Explicitly add python-numpy dependency to install-build-deps.
[chromium-blink-merge.git] / components / nacl / renderer / ppb_nacl_private_impl.cc
blob2df50bbf293e23782a26875353538d74a4b584ac
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 ~ManifestServiceProxy() override {}
170 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 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()), 0, 0));
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(), 0, 0);
228 return;
230 callback.Run(base::File(file_info.handle),
231 file_info.token_lo,
232 file_info.token_hi);
235 PP_Instance pp_instance_;
236 DISALLOW_COPY_AND_ASSIGN(ManifestServiceProxy);
239 blink::WebURLLoader* CreateWebURLLoader(const blink::WebDocument& document,
240 const GURL& gurl) {
241 blink::WebURLLoaderOptions options;
242 options.untrustedHTTP = true;
244 // Options settings here follow the original behavior in the trusted
245 // plugin and PepperURLLoaderHost.
246 if (document.securityOrigin().canRequest(gurl)) {
247 options.allowCredentials = true;
248 } else {
249 // Allow CORS.
250 options.crossOriginRequestPolicy =
251 blink::WebURLLoaderOptions::CrossOriginRequestPolicyUseAccessControl;
253 return document.frame()->createAssociatedURLLoader(options);
256 blink::WebURLRequest CreateWebURLRequest(const blink::WebDocument& document,
257 const GURL& gurl) {
258 blink::WebURLRequest request;
259 request.initialize();
260 request.setURL(gurl);
261 request.setFirstPartyForCookies(document.firstPartyForCookies());
262 return request;
265 int32_t FileDownloaderToPepperError(FileDownloader::Status status) {
266 switch (status) {
267 case FileDownloader::SUCCESS:
268 return PP_OK;
269 case FileDownloader::ACCESS_DENIED:
270 return PP_ERROR_NOACCESS;
271 case FileDownloader::FAILED:
272 return PP_ERROR_FAILED;
273 // No default case, to catch unhandled Status values.
275 return PP_ERROR_FAILED;
278 NaClAppProcessType PP_ToNaClAppProcessType(
279 PP_NaClAppProcessType pp_process_type) {
280 #define STATICALLY_CHECK_NACLAPPPROCESSTYPE_EQ(pp, nonpp) \
281 static_assert(static_cast<int>(pp) == static_cast<int>(nonpp), \
282 "PP_NaClAppProcessType differs from NaClAppProcessType");
283 STATICALLY_CHECK_NACLAPPPROCESSTYPE_EQ(PP_UNKNOWN_NACL_PROCESS_TYPE,
284 kUnknownNaClProcessType);
285 STATICALLY_CHECK_NACLAPPPROCESSTYPE_EQ(PP_NATIVE_NACL_PROCESS_TYPE,
286 kNativeNaClProcessType);
287 STATICALLY_CHECK_NACLAPPPROCESSTYPE_EQ(PP_PNACL_PROCESS_TYPE,
288 kPNaClProcessType);
289 STATICALLY_CHECK_NACLAPPPROCESSTYPE_EQ(PP_PNACL_TRANSLATOR_PROCESS_TYPE,
290 kPNaClTranslatorProcessType);
291 STATICALLY_CHECK_NACLAPPPROCESSTYPE_EQ(PP_NUM_NACL_PROCESS_TYPES,
292 kNumNaClProcessTypes);
293 #undef STATICALLY_CHECK_NACLAPPPROCESSTYPE_EQ
294 DCHECK(pp_process_type > PP_UNKNOWN_NACL_PROCESS_TYPE &&
295 pp_process_type < PP_NUM_NACL_PROCESS_TYPES);
296 return static_cast<NaClAppProcessType>(pp_process_type);
299 // Launch NaCl's sel_ldr process.
300 void LaunchSelLdr(PP_Instance instance,
301 PP_Bool main_service_runtime,
302 const char* alleged_url,
303 const PP_NaClFileInfo* nexe_file_info,
304 PP_Bool uses_nonsfi_mode,
305 PP_Bool enable_ppapi_dev,
306 PP_NaClAppProcessType pp_process_type,
307 void* imc_handle,
308 PP_CompletionCallback callback) {
309 CHECK(ppapi::PpapiGlobals::Get()->GetMainThreadMessageLoop()->
310 BelongsToCurrentThread());
311 NaClAppProcessType process_type = PP_ToNaClAppProcessType(pp_process_type);
312 // Create the manifest service proxy here, so on error case, it will be
313 // destructed (without passing it to ManifestServiceChannel).
314 scoped_ptr<ManifestServiceChannel::Delegate> manifest_service_proxy(
315 new ManifestServiceProxy(instance));
317 FileDescriptor result_socket;
318 IPC::Sender* sender = content::RenderThread::Get();
319 DCHECK(sender);
320 int routing_id = 0;
321 // If the nexe uses ppapi APIs, we need a routing ID.
322 // To get the routing ID, we must be on the main thread.
323 // Some nexes do not use ppapi and launch from the background thread,
324 // so those nexes can skip finding a routing_id. Currently, that only
325 // applies to the PNaClTranslatorProcesses.
326 bool uses_ppapi = process_type != kPNaClTranslatorProcessType;
327 if (uses_ppapi) {
328 routing_id = GetRoutingID(instance);
329 if (!routing_id) {
330 if (nexe_file_info->handle != PP_kInvalidFileHandle) {
331 base::File closer(nexe_file_info->handle);
333 ppapi::PpapiGlobals::Get()->GetMainThreadMessageLoop()->PostTask(
334 FROM_HERE,
335 base::Bind(callback.func, callback.user_data,
336 static_cast<int32_t>(PP_ERROR_FAILED)));
337 return;
341 InstanceInfo instance_info;
342 instance_info.url = GURL(alleged_url);
344 uint32_t perm_bits = ppapi::PERMISSION_NONE;
345 // Conditionally block 'Dev' interfaces. We do this for the NaCl process, so
346 // it's clearer to developers when they are using 'Dev' inappropriately. We
347 // must also check on the trusted side of the proxy.
348 if (enable_ppapi_dev)
349 perm_bits |= ppapi::PERMISSION_DEV;
350 instance_info.permissions =
351 ppapi::PpapiPermissions::GetForCommandLine(perm_bits);
352 std::string error_message_string;
353 NaClLaunchResult launch_result;
355 IPC::PlatformFileForTransit nexe_for_transit =
356 IPC::InvalidPlatformFileForTransit();
357 #if defined(OS_POSIX)
358 if (nexe_file_info->handle != PP_kInvalidFileHandle)
359 nexe_for_transit = base::FileDescriptor(nexe_file_info->handle, true);
360 #elif defined(OS_WIN)
361 // Duplicate the handle on the browser side instead of the renderer.
362 // This is because BrokerGetFileForProcess isn't part of content/public, and
363 // it's simpler to do the duplication in the browser anyway.
364 nexe_for_transit = nexe_file_info->handle;
365 #else
366 #error Unsupported target platform.
367 #endif
368 if (!sender->Send(new NaClHostMsg_LaunchNaCl(
369 NaClLaunchParams(
370 instance_info.url.spec(),
371 nexe_for_transit,
372 nexe_file_info->token_lo,
373 nexe_file_info->token_hi,
374 routing_id,
375 perm_bits,
376 PP_ToBool(uses_nonsfi_mode),
377 process_type),
378 &launch_result,
379 &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;
387 if (!error_message_string.empty()) {
388 if (PP_ToBool(main_service_runtime)) {
389 NexeLoadManager* load_manager = NexeLoadManager::Get(instance);
390 if (load_manager) {
391 load_manager->ReportLoadError(PP_NACL_ERROR_SEL_LDR_LAUNCH,
392 "ServiceRuntime: failed to start",
393 error_message_string);
396 ppapi::PpapiGlobals::Get()->GetMainThreadMessageLoop()->PostTask(
397 FROM_HERE,
398 base::Bind(callback.func, callback.user_data,
399 static_cast<int32_t>(PP_ERROR_FAILED)));
400 return;
402 result_socket = launch_result.imc_channel_handle;
403 instance_info.channel_handle = launch_result.ppapi_ipc_channel_handle;
404 instance_info.plugin_pid = launch_result.plugin_pid;
405 instance_info.plugin_child_id = launch_result.plugin_child_id;
407 // Don't save instance_info if channel handle is invalid.
408 if (IsValidChannelHandle(instance_info.channel_handle))
409 g_instance_info.Get()[instance] = instance_info;
411 *(static_cast<NaClHandle*>(imc_handle)) = ToNativeHandle(result_socket);
413 NexeLoadManager* load_manager = NexeLoadManager::Get(instance);
414 DCHECK(load_manager);
415 if (!load_manager) {
416 PostPPCompletionCallback(callback, PP_ERROR_FAILED);
417 base::SharedMemory::CloseHandle(launch_result.crash_info_shmem_handle);
418 return;
421 // Store the crash information shared memory handle.
422 load_manager->set_crash_info_shmem_handle(
423 launch_result.crash_info_shmem_handle);
425 // Create the trusted plugin channel.
426 if (IsValidChannelHandle(launch_result.trusted_ipc_channel_handle)) {
427 bool report_exit_status = PP_ToBool(main_service_runtime);
428 scoped_ptr<TrustedPluginChannel> trusted_plugin_channel(
429 new TrustedPluginChannel(
430 load_manager,
431 launch_result.trusted_ipc_channel_handle,
432 content::RenderThread::Get()->GetShutdownEvent(),
433 report_exit_status));
434 load_manager->set_trusted_plugin_channel(trusted_plugin_channel.Pass());
435 } else {
436 PostPPCompletionCallback(callback, PP_ERROR_FAILED);
437 return;
440 // Create the manifest service handle as well.
441 // For security hardening, disable the IPCs for open_resource() when they
442 // aren't needed. PNaCl doesn't expose open_resource().
443 if (load_manager &&
444 process_type == kNativeNaClProcessType &&
445 IsValidChannelHandle(
446 launch_result.manifest_service_ipc_channel_handle)) {
447 scoped_ptr<ManifestServiceChannel> manifest_service_channel(
448 new ManifestServiceChannel(
449 launch_result.manifest_service_ipc_channel_handle,
450 base::Bind(&PostPPCompletionCallback, callback),
451 manifest_service_proxy.Pass(),
452 content::RenderThread::Get()->GetShutdownEvent()));
453 load_manager->set_manifest_service_channel(
454 manifest_service_channel.Pass());
455 } else {
456 // Currently, manifest service works only on linux/non-SFI mode.
457 // On other platforms, the socket will not be created, and thus this
458 // condition needs to be handled as success.
459 PostPPCompletionCallback(callback, PP_OK);
463 PP_Bool StartPpapiProxy(PP_Instance instance) {
464 NexeLoadManager* load_manager = NexeLoadManager::Get(instance);
465 DCHECK(load_manager);
466 if (!load_manager)
467 return PP_FALSE;
469 content::PepperPluginInstance* plugin_instance =
470 content::PepperPluginInstance::Get(instance);
471 if (!plugin_instance) {
472 DLOG(ERROR) << "GetInstance() failed";
473 return PP_FALSE;
476 InstanceInfoMap& map = g_instance_info.Get();
477 InstanceInfoMap::iterator it = map.find(instance);
478 if (it == map.end()) {
479 DLOG(ERROR) << "Could not find instance ID";
480 return PP_FALSE;
482 InstanceInfo instance_info = it->second;
483 map.erase(it);
485 PP_ExternalPluginResult result = plugin_instance->SwitchToOutOfProcessProxy(
486 base::FilePath().AppendASCII(instance_info.url.spec()),
487 instance_info.permissions,
488 instance_info.channel_handle,
489 instance_info.plugin_pid,
490 instance_info.plugin_child_id);
492 if (result == PP_EXTERNAL_PLUGIN_OK) {
493 // Log the amound of time that has passed between the trusted plugin being
494 // initialized and the untrusted plugin being initialized. This is
495 // (roughly) the cost of using NaCl, in terms of startup time.
496 load_manager->ReportStartupOverhead();
497 return PP_TRUE;
498 } else if (result == PP_EXTERNAL_PLUGIN_ERROR_MODULE) {
499 load_manager->ReportLoadError(PP_NACL_ERROR_START_PROXY_MODULE,
500 "could not initialize module.");
501 } else if (result == PP_EXTERNAL_PLUGIN_ERROR_INSTANCE) {
502 load_manager->ReportLoadError(PP_NACL_ERROR_START_PROXY_MODULE,
503 "could not create instance.");
505 return PP_FALSE;
508 int UrandomFD(void) {
509 #if defined(OS_POSIX)
510 return base::GetUrandomFD();
511 #else
512 return -1;
513 #endif
516 PP_Bool Are3DInterfacesDisabled() {
517 return PP_FromBool(CommandLine::ForCurrentProcess()->HasSwitch(
518 switches::kDisable3DAPIs));
521 int32_t BrokerDuplicateHandle(PP_FileHandle source_handle,
522 uint32_t process_id,
523 PP_FileHandle* target_handle,
524 uint32_t desired_access,
525 uint32_t options) {
526 #if defined(OS_WIN)
527 return content::BrokerDuplicateHandle(source_handle, process_id,
528 target_handle, desired_access,
529 options);
530 #else
531 return 0;
532 #endif
535 // Convert a URL to a filename for GetReadonlyPnaclFd.
536 // Must be kept in sync with PnaclCanOpenFile() in
537 // components/nacl/browser/nacl_file_host.cc.
538 std::string PnaclComponentURLToFilename(const std::string& url) {
539 // PNaCl component URLs aren't arbitrary URLs; they are always either
540 // generated from ManifestResolveKey or PnaclResources::ReadResourceInfo.
541 // So, it's safe to just use string parsing operations here instead of
542 // URL-parsing ones.
543 DCHECK(StartsWithASCII(url, kPNaClTranslatorBaseUrl, true));
544 std::string r = url.substr(std::string(kPNaClTranslatorBaseUrl).length());
546 // Use white-listed-chars.
547 size_t replace_pos;
548 static const char* white_list = "abcdefghijklmnopqrstuvwxyz0123456789_";
549 replace_pos = r.find_first_not_of(white_list);
550 while(replace_pos != std::string::npos) {
551 r = r.replace(replace_pos, 1, "_");
552 replace_pos = r.find_first_not_of(white_list);
554 return r;
557 PP_FileHandle GetReadonlyPnaclFd(const char* url,
558 bool is_executable,
559 uint64_t* nonce_lo,
560 uint64_t* nonce_hi) {
561 std::string filename = PnaclComponentURLToFilename(url);
562 IPC::PlatformFileForTransit out_fd = IPC::InvalidPlatformFileForTransit();
563 IPC::Sender* sender = content::RenderThread::Get();
564 DCHECK(sender);
565 if (!sender->Send(new NaClHostMsg_GetReadonlyPnaclFD(
566 std::string(filename), is_executable,
567 &out_fd, nonce_lo, nonce_hi))) {
568 return PP_kInvalidFileHandle;
570 if (out_fd == IPC::InvalidPlatformFileForTransit()) {
571 return PP_kInvalidFileHandle;
573 return IPC::PlatformFileForTransitToPlatformFile(out_fd);
576 void GetReadExecPnaclFd(const char* url,
577 PP_NaClFileInfo* out_file_info) {
578 *out_file_info = kInvalidNaClFileInfo;
579 out_file_info->handle = GetReadonlyPnaclFd(url, true /* is_executable */,
580 &out_file_info->token_lo,
581 &out_file_info->token_hi);
584 PP_FileHandle CreateTemporaryFile(PP_Instance instance) {
585 IPC::PlatformFileForTransit transit_fd = IPC::InvalidPlatformFileForTransit();
586 IPC::Sender* sender = content::RenderThread::Get();
587 DCHECK(sender);
588 if (!sender->Send(new NaClHostMsg_NaClCreateTemporaryFile(
589 &transit_fd))) {
590 return PP_kInvalidFileHandle;
593 if (transit_fd == IPC::InvalidPlatformFileForTransit()) {
594 return PP_kInvalidFileHandle;
597 return IPC::PlatformFileForTransitToPlatformFile(transit_fd);
600 int32_t GetNumberOfProcessors() {
601 IPC::Sender* sender = content::RenderThread::Get();
602 DCHECK(sender);
603 int32_t num_processors = 1;
604 return sender->Send(new NaClHostMsg_NaClGetNumProcessors(&num_processors)) ?
605 num_processors : 1;
608 PP_Bool PPIsNonSFIModeEnabled() {
609 return PP_FromBool(IsNonSFIModeEnabled());
612 void GetNexeFd(PP_Instance instance,
613 const std::string& pexe_url,
614 uint32_t opt_level,
615 const base::Time& last_modified_time,
616 const std::string& etag,
617 bool has_no_store_header,
618 base::Callback<void(int32_t, bool, PP_FileHandle)> callback) {
619 if (!InitializePnaclResourceHost()) {
620 ppapi::PpapiGlobals::Get()->GetMainThreadMessageLoop()->PostTask(
621 FROM_HERE,
622 base::Bind(callback,
623 static_cast<int32_t>(PP_ERROR_FAILED),
624 false,
625 PP_kInvalidFileHandle));
626 return;
629 PnaclCacheInfo cache_info;
630 cache_info.pexe_url = GURL(pexe_url);
631 // TODO(dschuff): Get this value from the pnacl json file after it
632 // rolls in from NaCl.
633 cache_info.abi_version = 1;
634 cache_info.opt_level = opt_level;
635 cache_info.last_modified = last_modified_time;
636 cache_info.etag = etag;
637 cache_info.has_no_store_header = has_no_store_header;
638 cache_info.sandbox_isa = GetSandboxArch();
639 cache_info.extra_flags = GetCpuFeatures();
641 g_pnacl_resource_host.Get()->RequestNexeFd(
642 GetRoutingID(instance),
643 instance,
644 cache_info,
645 callback);
648 void ReportTranslationFinished(PP_Instance instance,
649 PP_Bool success,
650 int32_t opt_level,
651 int64_t pexe_size,
652 int64_t compile_time_us) {
653 if (success == PP_TRUE) {
654 static const int32_t kUnknownOptLevel = 4;
655 if (opt_level < 0 || opt_level > 3)
656 opt_level = kUnknownOptLevel;
657 HistogramEnumerate("NaCl.Options.PNaCl.OptLevel",
658 opt_level,
659 kUnknownOptLevel + 1);
660 HistogramKBPerSec("NaCl.Perf.PNaClLoadTime.CompileKBPerSec",
661 pexe_size / 1024,
662 compile_time_us);
663 HistogramSizeKB("NaCl.Perf.Size.Pexe", pexe_size / 1024);
665 NexeLoadManager* load_manager = NexeLoadManager::Get(instance);
666 if (load_manager) {
667 base::TimeDelta total_time = base::Time::Now() -
668 load_manager->pnacl_start_time();
669 HistogramTimeTranslation("NaCl.Perf.PNaClLoadTime.TotalUncachedTime",
670 total_time.InMilliseconds());
671 HistogramKBPerSec("NaCl.Perf.PNaClLoadTime.TotalUncachedKBPerSec",
672 pexe_size / 1024,
673 total_time.InMicroseconds());
677 // If the resource host isn't initialized, don't try to do that here.
678 // Just return because something is already very wrong.
679 if (g_pnacl_resource_host.Get().get() == NULL)
680 return;
681 g_pnacl_resource_host.Get()->ReportTranslationFinished(instance, success);
684 PP_FileHandle OpenNaClExecutable(PP_Instance instance,
685 const char* file_url,
686 uint64_t* nonce_lo,
687 uint64_t* nonce_hi) {
688 // Fast path only works for installed file URLs.
689 GURL gurl(file_url);
690 if (!gurl.SchemeIs("chrome-extension"))
691 return PP_kInvalidFileHandle;
693 content::PepperPluginInstance* plugin_instance =
694 content::PepperPluginInstance::Get(instance);
695 if (!plugin_instance)
696 return PP_kInvalidFileHandle;
697 // IMPORTANT: Make sure the document can request the given URL. If we don't
698 // check, a malicious app could probe the extension system. This enforces a
699 // same-origin policy which prevents the app from requesting resources from
700 // another app.
701 blink::WebSecurityOrigin security_origin =
702 plugin_instance->GetContainer()->element().document().securityOrigin();
703 if (!security_origin.canRequest(gurl))
704 return PP_kInvalidFileHandle;
706 IPC::PlatformFileForTransit out_fd = IPC::InvalidPlatformFileForTransit();
707 IPC::Sender* sender = content::RenderThread::Get();
708 DCHECK(sender);
709 *nonce_lo = 0;
710 *nonce_hi = 0;
711 base::FilePath file_path;
712 if (!sender->Send(
713 new NaClHostMsg_OpenNaClExecutable(GetRoutingID(instance),
714 GURL(file_url),
715 &out_fd,
716 nonce_lo,
717 nonce_hi))) {
718 return PP_kInvalidFileHandle;
721 if (out_fd == IPC::InvalidPlatformFileForTransit())
722 return PP_kInvalidFileHandle;
724 return IPC::PlatformFileForTransitToPlatformFile(out_fd);
727 void DispatchEvent(PP_Instance instance,
728 PP_NaClEventType event_type,
729 const char *resource_url,
730 PP_Bool length_is_computable,
731 uint64_t loaded_bytes,
732 uint64_t total_bytes) {
733 ProgressEvent event(event_type,
734 resource_url,
735 PP_ToBool(length_is_computable),
736 loaded_bytes,
737 total_bytes);
738 DispatchProgressEvent(instance, event);
741 void ReportLoadSuccess(PP_Instance instance,
742 uint64_t loaded_bytes,
743 uint64_t total_bytes) {
744 NexeLoadManager* load_manager = NexeLoadManager::Get(instance);
745 if (load_manager) {
746 load_manager->ReportLoadSuccess(load_manager->program_url(),
747 loaded_bytes,
748 total_bytes);
752 void ReportLoadError(PP_Instance instance,
753 PP_NaClError error,
754 const char* error_message) {
755 NexeLoadManager* load_manager = NexeLoadManager::Get(instance);
756 if (load_manager)
757 load_manager->ReportLoadError(error, error_message);
760 void ReportLoadAbort(PP_Instance instance) {
761 NexeLoadManager* load_manager = NexeLoadManager::Get(instance);
762 if (load_manager)
763 load_manager->ReportLoadAbort();
766 void InstanceCreated(PP_Instance instance) {
767 NexeLoadManager::Create(instance);
770 void InstanceDestroyed(PP_Instance instance) {
771 DeleteJsonManifest(instance);
772 NexeLoadManager::Delete(instance);
775 PP_Bool NaClDebugEnabledForURL(const char* alleged_nmf_url) {
776 if (!CommandLine::ForCurrentProcess()->HasSwitch(switches::kEnableNaClDebug))
777 return PP_FALSE;
778 IPC::Sender* sender = content::RenderThread::Get();
779 DCHECK(sender);
780 bool should_debug = false;
781 return PP_FromBool(
782 sender->Send(new NaClHostMsg_NaClDebugEnabledForURL(GURL(alleged_nmf_url),
783 &should_debug)) &&
784 should_debug);
787 void LogToConsole(PP_Instance instance, const char* message) {
788 NexeLoadManager* load_manager = NexeLoadManager::Get(instance);
789 DCHECK(load_manager);
790 if (load_manager)
791 load_manager->LogToConsole(std::string(message));
794 PP_NaClReadyState GetNaClReadyState(PP_Instance instance) {
795 NexeLoadManager* load_manager = NexeLoadManager::Get(instance);
796 DCHECK(load_manager);
797 if (load_manager)
798 return load_manager->nacl_ready_state();
799 return PP_NACL_READY_STATE_UNSENT;
802 void Vlog(const char* message) {
803 VLOG(1) << message;
806 void InitializePlugin(PP_Instance instance,
807 uint32_t argc,
808 const char* argn[],
809 const char* argv[]) {
810 NexeLoadManager* load_manager = NexeLoadManager::Get(instance);
811 DCHECK(load_manager);
812 if (load_manager)
813 load_manager->InitializePlugin(argc, argn, argv);
816 int64_t GetNexeSize(PP_Instance instance) {
817 NexeLoadManager* load_manager = NexeLoadManager::Get(instance);
818 DCHECK(load_manager);
819 if (load_manager)
820 return load_manager->nexe_size();
821 return 0;
824 void DownloadManifestToBuffer(PP_Instance instance,
825 struct PP_CompletionCallback callback);
827 bool CreateJsonManifest(PP_Instance instance,
828 const std::string& manifest_url,
829 const std::string& manifest_data);
831 void RequestNaClManifest(PP_Instance instance,
832 PP_CompletionCallback callback) {
833 NexeLoadManager* load_manager = NexeLoadManager::Get(instance);
834 DCHECK(load_manager);
835 if (!load_manager) {
836 ppapi::PpapiGlobals::Get()->GetMainThreadMessageLoop()->PostTask(
837 FROM_HERE,
838 base::Bind(callback.func, callback.user_data,
839 static_cast<int32_t>(PP_ERROR_FAILED)));
840 return;
843 std::string url = load_manager->GetManifestURLArgument();
844 if (url.empty() || !load_manager->RequestNaClManifest(url)) {
845 ppapi::PpapiGlobals::Get()->GetMainThreadMessageLoop()->PostTask(
846 FROM_HERE,
847 base::Bind(callback.func, callback.user_data,
848 static_cast<int32_t>(PP_ERROR_FAILED)));
849 return;
852 const GURL& base_url = load_manager->manifest_base_url();
853 if (base_url.SchemeIs("data")) {
854 GURL gurl(base_url);
855 std::string mime_type;
856 std::string charset;
857 std::string data;
858 int32_t error = PP_ERROR_FAILED;
859 if (net::DataURL::Parse(gurl, &mime_type, &charset, &data)) {
860 if (data.size() <= ManifestDownloader::kNaClManifestMaxFileBytes) {
861 if (CreateJsonManifest(instance, base_url.spec(), data))
862 error = PP_OK;
863 } else {
864 load_manager->ReportLoadError(PP_NACL_ERROR_MANIFEST_TOO_LARGE,
865 "manifest file too large.");
867 } else {
868 load_manager->ReportLoadError(PP_NACL_ERROR_MANIFEST_LOAD_URL,
869 "could not load manifest url.");
871 ppapi::PpapiGlobals::Get()->GetMainThreadMessageLoop()->PostTask(
872 FROM_HERE,
873 base::Bind(callback.func, callback.user_data, error));
874 } else {
875 DownloadManifestToBuffer(instance, callback);
879 PP_Var GetManifestBaseURL(PP_Instance instance) {
880 NexeLoadManager* load_manager = NexeLoadManager::Get(instance);
881 DCHECK(load_manager);
882 if (!load_manager)
883 return PP_MakeUndefined();
884 const GURL& gurl = load_manager->manifest_base_url();
885 if (!gurl.is_valid())
886 return PP_MakeUndefined();
887 return ppapi::StringVar::StringToPPVar(gurl.spec());
890 void ProcessNaClManifest(PP_Instance instance, const char* program_url) {
891 nacl::NexeLoadManager* load_manager = NexeLoadManager::Get(instance);
892 if (load_manager)
893 load_manager->ProcessNaClManifest(program_url);
896 PP_Bool DevInterfacesEnabled(PP_Instance instance) {
897 nacl::NexeLoadManager* load_manager = NexeLoadManager::Get(instance);
898 if (load_manager)
899 return PP_FromBool(load_manager->DevInterfacesEnabled());
900 return PP_FALSE;
903 void DownloadManifestToBufferCompletion(PP_Instance instance,
904 struct PP_CompletionCallback callback,
905 base::Time start_time,
906 PP_NaClError pp_nacl_error,
907 const std::string& data);
909 void DownloadManifestToBuffer(PP_Instance instance,
910 struct PP_CompletionCallback callback) {
911 nacl::NexeLoadManager* load_manager = NexeLoadManager::Get(instance);
912 DCHECK(load_manager);
913 content::PepperPluginInstance* plugin_instance =
914 content::PepperPluginInstance::Get(instance);
915 if (!load_manager || !plugin_instance) {
916 ppapi::PpapiGlobals::Get()->GetMainThreadMessageLoop()->PostTask(
917 FROM_HERE,
918 base::Bind(callback.func, callback.user_data,
919 static_cast<int32_t>(PP_ERROR_FAILED)));
921 const blink::WebDocument& document =
922 plugin_instance->GetContainer()->element().document();
924 const GURL& gurl = load_manager->manifest_base_url();
925 scoped_ptr<blink::WebURLLoader> url_loader(
926 CreateWebURLLoader(document, gurl));
927 blink::WebURLRequest request = CreateWebURLRequest(document, gurl);
929 // ManifestDownloader deletes itself after invoking the callback.
930 ManifestDownloader* manifest_downloader = new ManifestDownloader(
931 url_loader.Pass(),
932 load_manager->is_installed(),
933 base::Bind(DownloadManifestToBufferCompletion,
934 instance, callback, base::Time::Now()));
935 manifest_downloader->Load(request);
938 void DownloadManifestToBufferCompletion(PP_Instance instance,
939 struct PP_CompletionCallback callback,
940 base::Time start_time,
941 PP_NaClError pp_nacl_error,
942 const std::string& data) {
943 base::TimeDelta download_time = base::Time::Now() - start_time;
944 HistogramTimeSmall("NaCl.Perf.StartupTime.ManifestDownload",
945 download_time.InMilliseconds());
947 nacl::NexeLoadManager* load_manager = NexeLoadManager::Get(instance);
948 if (!load_manager) {
949 callback.func(callback.user_data, PP_ERROR_ABORTED);
950 return;
953 int32_t pp_error;
954 switch (pp_nacl_error) {
955 case PP_NACL_ERROR_LOAD_SUCCESS:
956 pp_error = PP_OK;
957 break;
958 case PP_NACL_ERROR_MANIFEST_LOAD_URL:
959 pp_error = PP_ERROR_FAILED;
960 load_manager->ReportLoadError(PP_NACL_ERROR_MANIFEST_LOAD_URL,
961 "could not load manifest url.");
962 break;
963 case PP_NACL_ERROR_MANIFEST_TOO_LARGE:
964 pp_error = PP_ERROR_FILETOOBIG;
965 load_manager->ReportLoadError(PP_NACL_ERROR_MANIFEST_TOO_LARGE,
966 "manifest file too large.");
967 break;
968 case PP_NACL_ERROR_MANIFEST_NOACCESS_URL:
969 pp_error = PP_ERROR_NOACCESS;
970 load_manager->ReportLoadError(PP_NACL_ERROR_MANIFEST_NOACCESS_URL,
971 "access to manifest url was denied.");
972 break;
973 default:
974 NOTREACHED();
975 pp_error = PP_ERROR_FAILED;
976 load_manager->ReportLoadError(PP_NACL_ERROR_MANIFEST_LOAD_URL,
977 "could not load manifest url.");
980 if (pp_error == PP_OK) {
981 std::string base_url = load_manager->manifest_base_url().spec();
982 if (!CreateJsonManifest(instance, base_url, data))
983 pp_error = PP_ERROR_FAILED;
985 callback.func(callback.user_data, pp_error);
988 bool CreateJsonManifest(PP_Instance instance,
989 const std::string& manifest_url,
990 const std::string& manifest_data) {
991 HistogramSizeKB("NaCl.Perf.Size.Manifest",
992 static_cast<int32_t>(manifest_data.length() / 1024));
994 nacl::NexeLoadManager* load_manager = NexeLoadManager::Get(instance);
995 if (!load_manager)
996 return false;
998 const char* isa_type;
999 if (load_manager->IsPNaCl())
1000 isa_type = kPortableArch;
1001 else
1002 isa_type = GetSandboxArch();
1004 scoped_ptr<nacl::JsonManifest> j(
1005 new nacl::JsonManifest(
1006 manifest_url.c_str(),
1007 isa_type,
1008 IsNonSFIModeEnabled(),
1009 PP_ToBool(NaClDebugEnabledForURL(manifest_url.c_str()))));
1010 JsonManifest::ErrorInfo error_info;
1011 if (j->Init(manifest_data.c_str(), &error_info)) {
1012 AddJsonManifest(instance, j.Pass());
1013 return true;
1015 load_manager->ReportLoadError(error_info.error, error_info.string);
1016 return false;
1019 PP_Bool ManifestGetProgramURL(PP_Instance instance,
1020 PP_Var* pp_full_url,
1021 PP_PNaClOptions* pnacl_options,
1022 PP_Bool* pp_uses_nonsfi_mode) {
1023 nacl::NexeLoadManager* load_manager = NexeLoadManager::Get(instance);
1025 JsonManifest* manifest = GetJsonManifest(instance);
1026 if (manifest == NULL)
1027 return PP_FALSE;
1029 bool uses_nonsfi_mode;
1030 std::string full_url;
1031 JsonManifest::ErrorInfo error_info;
1032 if (manifest->GetProgramURL(&full_url, pnacl_options, &uses_nonsfi_mode,
1033 &error_info)) {
1034 *pp_full_url = ppapi::StringVar::StringToPPVar(full_url);
1035 *pp_uses_nonsfi_mode = PP_FromBool(uses_nonsfi_mode);
1036 return PP_TRUE;
1039 if (load_manager)
1040 load_manager->ReportLoadError(error_info.error, error_info.string);
1041 return PP_FALSE;
1044 bool ManifestResolveKey(PP_Instance instance,
1045 bool is_helper_process,
1046 const std::string& key,
1047 std::string* full_url,
1048 PP_PNaClOptions* pnacl_options) {
1049 // For "helper" processes (llc and ld, for PNaCl translation), we resolve
1050 // keys manually as there is no existing .nmf file to parse.
1051 if (is_helper_process) {
1052 pnacl_options->translate = PP_FALSE;
1053 // We can only resolve keys in the files/ namespace.
1054 const std::string kFilesPrefix = "files/";
1055 if (key.find(kFilesPrefix) == std::string::npos) {
1056 nacl::NexeLoadManager* load_manager = NexeLoadManager::Get(instance);
1057 if (load_manager)
1058 load_manager->ReportLoadError(PP_NACL_ERROR_MANIFEST_RESOLVE_URL,
1059 "key did not start with files/");
1060 return false;
1062 std::string key_basename = key.substr(kFilesPrefix.length());
1063 *full_url = std::string(kPNaClTranslatorBaseUrl) + GetSandboxArch() + "/" +
1064 key_basename;
1065 return true;
1068 JsonManifest* manifest = GetJsonManifest(instance);
1069 if (manifest == NULL)
1070 return false;
1072 return manifest->ResolveKey(key, full_url, pnacl_options);
1075 PP_Bool GetPNaClResourceInfo(PP_Instance instance,
1076 PP_Var* llc_tool_name,
1077 PP_Var* ld_tool_name) {
1078 static const char kFilename[] = "chrome://pnacl-translator/pnacl.json";
1079 NexeLoadManager* load_manager = NexeLoadManager::Get(instance);
1080 DCHECK(load_manager);
1081 if (!load_manager)
1082 return PP_FALSE;
1084 uint64_t nonce_lo = 0;
1085 uint64_t nonce_hi = 0;
1086 base::File file(GetReadonlyPnaclFd(kFilename, false /* is_executable */,
1087 &nonce_lo, &nonce_hi));
1088 if (!file.IsValid()) {
1089 load_manager->ReportLoadError(
1090 PP_NACL_ERROR_PNACL_RESOURCE_FETCH,
1091 "The Portable Native Client (pnacl) component is not "
1092 "installed. Please consult chrome://components for more "
1093 "information.");
1094 return PP_FALSE;
1097 base::File::Info file_info;
1098 if (!file.GetInfo(&file_info)) {
1099 load_manager->ReportLoadError(
1100 PP_NACL_ERROR_PNACL_RESOURCE_FETCH,
1101 std::string("GetPNaClResourceInfo, GetFileInfo failed for: ") +
1102 kFilename);
1103 return PP_FALSE;
1106 if (file_info.size > 1 << 20) {
1107 load_manager->ReportLoadError(
1108 PP_NACL_ERROR_PNACL_RESOURCE_FETCH,
1109 std::string("GetPNaClResourceInfo, file too large: ") + kFilename);
1110 return PP_FALSE;
1113 scoped_ptr<char[]> buffer(new char[file_info.size + 1]);
1114 if (buffer.get() == NULL) {
1115 load_manager->ReportLoadError(
1116 PP_NACL_ERROR_PNACL_RESOURCE_FETCH,
1117 std::string("GetPNaClResourceInfo, couldn't allocate for: ") +
1118 kFilename);
1119 return PP_FALSE;
1122 int rc = file.Read(0, buffer.get(), file_info.size);
1123 if (rc < 0) {
1124 load_manager->ReportLoadError(
1125 PP_NACL_ERROR_PNACL_RESOURCE_FETCH,
1126 std::string("GetPNaClResourceInfo, reading failed for: ") + kFilename);
1127 return PP_FALSE;
1130 // Null-terminate the bytes we we read from the file.
1131 buffer.get()[rc] = 0;
1133 // Expect the JSON file to contain a top-level object (dictionary).
1134 Json::Reader json_reader;
1135 Json::Value json_data;
1136 if (!json_reader.parse(buffer.get(), json_data)) {
1137 load_manager->ReportLoadError(
1138 PP_NACL_ERROR_PNACL_RESOURCE_FETCH,
1139 std::string("Parsing resource info failed: JSON parse error: ") +
1140 json_reader.getFormattedErrorMessages());
1141 return PP_FALSE;
1144 if (!json_data.isObject()) {
1145 load_manager->ReportLoadError(
1146 PP_NACL_ERROR_PNACL_RESOURCE_FETCH,
1147 "Parsing resource info failed: Malformed JSON dictionary");
1148 return PP_FALSE;
1151 if (json_data.isMember("pnacl-llc-name")) {
1152 Json::Value json_name = json_data["pnacl-llc-name"];
1153 if (json_name.isString()) {
1154 std::string llc_tool_name_str = json_name.asString();
1155 *llc_tool_name = ppapi::StringVar::StringToPPVar(llc_tool_name_str);
1159 if (json_data.isMember("pnacl-ld-name")) {
1160 Json::Value json_name = json_data["pnacl-ld-name"];
1161 if (json_name.isString()) {
1162 std::string ld_tool_name_str = json_name.asString();
1163 *ld_tool_name = ppapi::StringVar::StringToPPVar(ld_tool_name_str);
1166 return PP_TRUE;
1169 PP_Var GetCpuFeatureAttrs() {
1170 return ppapi::StringVar::StringToPPVar(GetCpuFeatures());
1173 // Encapsulates some of the state for a call to DownloadNexe to prevent
1174 // argument lists from getting too long.
1175 struct DownloadNexeRequest {
1176 PP_Instance instance;
1177 std::string url;
1178 PP_CompletionCallback callback;
1179 base::Time start_time;
1182 // A utility class to ensure that we don't send progress events more often than
1183 // every 10ms for a given file.
1184 class ProgressEventRateLimiter {
1185 public:
1186 explicit ProgressEventRateLimiter(PP_Instance instance)
1187 : instance_(instance) { }
1189 void ReportProgress(const std::string& url,
1190 int64_t total_bytes_received,
1191 int64_t total_bytes_to_be_received) {
1192 base::Time now = base::Time::Now();
1193 if (now - last_event_ > base::TimeDelta::FromMilliseconds(10)) {
1194 DispatchProgressEvent(instance_,
1195 ProgressEvent(PP_NACL_EVENT_PROGRESS,
1196 url,
1197 total_bytes_to_be_received >= 0,
1198 total_bytes_received,
1199 total_bytes_to_be_received));
1200 last_event_ = now;
1204 private:
1205 PP_Instance instance_;
1206 base::Time last_event_;
1209 void DownloadNexeCompletion(const DownloadNexeRequest& request,
1210 PP_NaClFileInfo* out_file_info,
1211 FileDownloader::Status status,
1212 base::File target_file,
1213 int http_status);
1215 void DownloadNexe(PP_Instance instance,
1216 const char* url,
1217 PP_NaClFileInfo* out_file_info,
1218 PP_CompletionCallback callback) {
1219 CHECK(url);
1220 CHECK(out_file_info);
1221 DownloadNexeRequest request;
1222 request.instance = instance;
1223 request.url = url;
1224 request.callback = callback;
1225 request.start_time = base::Time::Now();
1227 // Try the fast path for retrieving the file first.
1228 PP_FileHandle handle = OpenNaClExecutable(instance,
1229 url,
1230 &out_file_info->token_lo,
1231 &out_file_info->token_hi);
1232 if (handle != PP_kInvalidFileHandle) {
1233 DownloadNexeCompletion(request,
1234 out_file_info,
1235 FileDownloader::SUCCESS,
1236 base::File(handle),
1237 200);
1238 return;
1241 // The fast path didn't work, we'll fetch the file using URLLoader and write
1242 // it to local storage.
1243 base::File target_file(CreateTemporaryFile(instance));
1244 GURL gurl(url);
1246 content::PepperPluginInstance* plugin_instance =
1247 content::PepperPluginInstance::Get(instance);
1248 if (!plugin_instance) {
1249 ppapi::PpapiGlobals::Get()->GetMainThreadMessageLoop()->PostTask(
1250 FROM_HERE,
1251 base::Bind(callback.func, callback.user_data,
1252 static_cast<int32_t>(PP_ERROR_FAILED)));
1254 const blink::WebDocument& document =
1255 plugin_instance->GetContainer()->element().document();
1256 scoped_ptr<blink::WebURLLoader> url_loader(
1257 CreateWebURLLoader(document, gurl));
1258 blink::WebURLRequest url_request = CreateWebURLRequest(document, gurl);
1260 ProgressEventRateLimiter* tracker = new ProgressEventRateLimiter(instance);
1262 // FileDownloader deletes itself after invoking DownloadNexeCompletion.
1263 FileDownloader* file_downloader = new FileDownloader(
1264 url_loader.Pass(),
1265 target_file.Pass(),
1266 base::Bind(&DownloadNexeCompletion, request, out_file_info),
1267 base::Bind(&ProgressEventRateLimiter::ReportProgress,
1268 base::Owned(tracker), std::string(url)));
1269 file_downloader->Load(url_request);
1272 void DownloadNexeCompletion(const DownloadNexeRequest& request,
1273 PP_NaClFileInfo* out_file_info,
1274 FileDownloader::Status status,
1275 base::File target_file,
1276 int http_status) {
1277 int32_t pp_error = FileDownloaderToPepperError(status);
1278 int64_t bytes_read = -1;
1279 if (pp_error == PP_OK && target_file.IsValid()) {
1280 base::File::Info info;
1281 if (target_file.GetInfo(&info))
1282 bytes_read = info.size;
1285 if (bytes_read == -1) {
1286 target_file.Close();
1287 pp_error = PP_ERROR_FAILED;
1290 base::TimeDelta download_time = base::Time::Now() - request.start_time;
1292 NexeLoadManager* load_manager = NexeLoadManager::Get(request.instance);
1293 if (load_manager) {
1294 load_manager->NexeFileDidOpen(pp_error,
1295 target_file,
1296 http_status,
1297 bytes_read,
1298 request.url,
1299 download_time);
1302 if (pp_error == PP_OK && target_file.IsValid())
1303 out_file_info->handle = target_file.TakePlatformFile();
1304 else
1305 out_file_info->handle = PP_kInvalidFileHandle;
1307 request.callback.func(request.callback.user_data, pp_error);
1310 void DownloadFileCompletion(
1311 const DownloadFileCallback& callback,
1312 FileDownloader::Status status,
1313 base::File file,
1314 int http_status) {
1315 int32_t pp_error = FileDownloaderToPepperError(status);
1316 PP_NaClFileInfo file_info;
1317 if (pp_error == PP_OK) {
1318 file_info.handle = file.TakePlatformFile();
1319 file_info.token_lo = 0;
1320 file_info.token_hi = 0;
1321 } else {
1322 file_info = kInvalidNaClFileInfo;
1325 callback.Run(pp_error, file_info);
1328 void DownloadFile(PP_Instance instance,
1329 const std::string& url,
1330 const DownloadFileCallback& callback) {
1331 DCHECK(ppapi::PpapiGlobals::Get()->GetMainThreadMessageLoop()->
1332 BelongsToCurrentThread());
1334 NexeLoadManager* load_manager = NexeLoadManager::Get(instance);
1335 DCHECK(load_manager);
1336 if (!load_manager) {
1337 base::MessageLoop::current()->PostTask(
1338 FROM_HERE,
1339 base::Bind(callback,
1340 static_cast<int32_t>(PP_ERROR_FAILED),
1341 kInvalidNaClFileInfo));
1342 return;
1345 // Handle special PNaCl support files which are installed on the user's
1346 // machine.
1347 if (url.find(kPNaClTranslatorBaseUrl, 0) == 0) {
1348 PP_NaClFileInfo file_info = kInvalidNaClFileInfo;
1349 PP_FileHandle handle = GetReadonlyPnaclFd(url.c_str(),
1350 false /* is_executable */,
1351 &file_info.token_lo,
1352 &file_info.token_hi);
1353 if (handle == PP_kInvalidFileHandle) {
1354 base::MessageLoop::current()->PostTask(
1355 FROM_HERE,
1356 base::Bind(callback,
1357 static_cast<int32_t>(PP_ERROR_FAILED),
1358 kInvalidNaClFileInfo));
1359 return;
1361 file_info.handle = handle;
1362 base::MessageLoop::current()->PostTask(
1363 FROM_HERE,
1364 base::Bind(callback, static_cast<int32_t>(PP_OK), file_info));
1365 return;
1368 // We have to ensure that this url resolves relative to the plugin base url
1369 // before downloading it.
1370 const GURL& test_gurl = load_manager->plugin_base_url().Resolve(url);
1371 if (!test_gurl.is_valid()) {
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 // Try the fast path for retrieving the file first.
1381 uint64_t file_token_lo = 0;
1382 uint64_t file_token_hi = 0;
1383 PP_FileHandle file_handle = OpenNaClExecutable(instance,
1384 url.c_str(),
1385 &file_token_lo,
1386 &file_token_hi);
1387 if (file_handle != PP_kInvalidFileHandle) {
1388 PP_NaClFileInfo file_info;
1389 file_info.handle = file_handle;
1390 file_info.token_lo = file_token_lo;
1391 file_info.token_hi = file_token_hi;
1392 base::MessageLoop::current()->PostTask(
1393 FROM_HERE,
1394 base::Bind(callback, static_cast<int32_t>(PP_OK), file_info));
1395 return;
1398 // The fast path didn't work, we'll fetch the file using URLLoader and write
1399 // it to local storage.
1400 base::File target_file(CreateTemporaryFile(instance));
1401 GURL gurl(url);
1403 content::PepperPluginInstance* plugin_instance =
1404 content::PepperPluginInstance::Get(instance);
1405 if (!plugin_instance) {
1406 base::MessageLoop::current()->PostTask(
1407 FROM_HERE,
1408 base::Bind(callback,
1409 static_cast<int32_t>(PP_ERROR_FAILED),
1410 kInvalidNaClFileInfo));
1412 const blink::WebDocument& document =
1413 plugin_instance->GetContainer()->element().document();
1414 scoped_ptr<blink::WebURLLoader> url_loader(
1415 CreateWebURLLoader(document, gurl));
1416 blink::WebURLRequest url_request = CreateWebURLRequest(document, gurl);
1418 ProgressEventRateLimiter* tracker = new ProgressEventRateLimiter(instance);
1420 // FileDownloader deletes itself after invoking DownloadNexeCompletion.
1421 FileDownloader* file_downloader = new FileDownloader(
1422 url_loader.Pass(),
1423 target_file.Pass(),
1424 base::Bind(&DownloadFileCompletion, callback),
1425 base::Bind(&ProgressEventRateLimiter::ReportProgress,
1426 base::Owned(tracker), std::string(url)));
1427 file_downloader->Load(url_request);
1430 void ReportSelLdrStatus(PP_Instance instance,
1431 int32_t load_status,
1432 int32_t max_status) {
1433 HistogramEnumerate("NaCl.LoadStatus.SelLdr", load_status, max_status);
1434 NexeLoadManager* load_manager = NexeLoadManager::Get(instance);
1435 DCHECK(load_manager);
1436 if (!load_manager)
1437 return;
1439 // Gather data to see if being installed changes load outcomes.
1440 const char* name = load_manager->is_installed() ?
1441 "NaCl.LoadStatus.SelLdr.InstalledApp" :
1442 "NaCl.LoadStatus.SelLdr.NotInstalledApp";
1443 HistogramEnumerate(name, load_status, max_status);
1446 void LogTranslateTime(const char* histogram_name,
1447 int64_t time_in_us) {
1448 ppapi::PpapiGlobals::Get()->GetMainThreadMessageLoop()->PostTask(
1449 FROM_HERE,
1450 base::Bind(&HistogramTimeTranslation,
1451 std::string(histogram_name),
1452 time_in_us / 1000));
1455 void DidOpenManifestEntry(PP_NaClFileInfo* out_file_info,
1456 PP_CompletionCallback callback,
1457 int32_t pp_error,
1458 const PP_NaClFileInfo& file_info) {
1459 if (pp_error == PP_OK)
1460 *out_file_info = file_info;
1461 callback.func(callback.user_data, pp_error);
1464 void OpenManifestEntry(PP_Instance instance,
1465 PP_Bool is_helper_process,
1466 const char* key,
1467 PP_NaClFileInfo* out_file_info,
1468 PP_CompletionCallback callback) {
1469 std::string url;
1470 PP_PNaClOptions pnacl_options;
1471 pnacl_options.translate = PP_FALSE;
1472 pnacl_options.is_debug = PP_FALSE;
1473 pnacl_options.opt_level = 2;
1474 if (!ManifestResolveKey(instance,
1475 PP_ToBool(is_helper_process),
1476 key,
1477 &url,
1478 &pnacl_options)) {
1479 PostPPCompletionCallback(callback, PP_ERROR_FAILED);
1482 // TODO(teravest): Make a type like PP_NaClFileInfo to use for DownloadFile
1483 // that would close the file handle on destruction.
1484 DownloadFile(instance, url,
1485 base::Bind(&DidOpenManifestEntry, out_file_info, callback));
1488 void SetPNaClStartTime(PP_Instance instance) {
1489 NexeLoadManager* load_manager = NexeLoadManager::Get(instance);
1490 if (load_manager)
1491 load_manager->set_pnacl_start_time(base::Time::Now());
1494 // PexeDownloader is responsible for deleting itself when the download
1495 // finishes.
1496 class PexeDownloader : public blink::WebURLLoaderClient {
1497 public:
1498 PexeDownloader(PP_Instance instance,
1499 scoped_ptr<blink::WebURLLoader> url_loader,
1500 const std::string& pexe_url,
1501 int32_t pexe_opt_level,
1502 const PPP_PexeStreamHandler* stream_handler,
1503 void* stream_handler_user_data)
1504 : instance_(instance),
1505 url_loader_(url_loader.Pass()),
1506 pexe_url_(pexe_url),
1507 pexe_opt_level_(pexe_opt_level),
1508 stream_handler_(stream_handler),
1509 stream_handler_user_data_(stream_handler_user_data),
1510 success_(false),
1511 expected_content_length_(-1),
1512 weak_factory_(this) { }
1514 void Load(const blink::WebURLRequest& request) {
1515 url_loader_->loadAsynchronously(request, this);
1518 private:
1519 virtual void didReceiveResponse(blink::WebURLLoader* loader,
1520 const blink::WebURLResponse& response) {
1521 success_ = (response.httpStatusCode() == 200);
1522 if (!success_)
1523 return;
1525 expected_content_length_ = response.expectedContentLength();
1527 // Defer loading after receiving headers. This is because we may already
1528 // have a cached translated nexe, so check for that now.
1529 url_loader_->setDefersLoading(true);
1531 std::string etag = response.httpHeaderField("etag").utf8();
1532 std::string last_modified =
1533 response.httpHeaderField("last-modified").utf8();
1534 base::Time last_modified_time;
1535 base::Time::FromString(last_modified.c_str(), &last_modified_time);
1537 bool has_no_store_header = false;
1538 std::string cache_control =
1539 response.httpHeaderField("cache-control").utf8();
1541 std::vector<std::string> values;
1542 base::SplitString(cache_control, ',', &values);
1543 for (std::vector<std::string>::const_iterator it = values.begin();
1544 it != values.end();
1545 ++it) {
1546 if (base::StringToLowerASCII(*it) == "no-store")
1547 has_no_store_header = true;
1550 GetNexeFd(instance_,
1551 pexe_url_,
1552 pexe_opt_level_,
1553 last_modified_time,
1554 etag,
1555 has_no_store_header,
1556 base::Bind(&PexeDownloader::didGetNexeFd,
1557 weak_factory_.GetWeakPtr()));
1560 virtual void didGetNexeFd(int32_t pp_error,
1561 bool cache_hit,
1562 PP_FileHandle file_handle) {
1563 if (!content::PepperPluginInstance::Get(instance_)) {
1564 delete this;
1565 return;
1568 HistogramEnumerate("NaCl.Perf.PNaClCache.IsHit", cache_hit, 2);
1569 if (cache_hit) {
1570 stream_handler_->DidCacheHit(stream_handler_user_data_, file_handle);
1572 // We delete the PexeDownloader at this point since we successfully got a
1573 // cached, translated nexe.
1574 delete this;
1575 return;
1577 stream_handler_->DidCacheMiss(stream_handler_user_data_,
1578 expected_content_length_,
1579 file_handle);
1581 // No translated nexe was found in the cache, so we should download the
1582 // file to start streaming it.
1583 url_loader_->setDefersLoading(false);
1586 virtual void didReceiveData(blink::WebURLLoader* loader,
1587 const char* data,
1588 int data_length,
1589 int encoded_data_length) {
1590 if (content::PepperPluginInstance::Get(instance_)) {
1591 // Stream the data we received to the stream callback.
1592 stream_handler_->DidStreamData(stream_handler_user_data_,
1593 data,
1594 data_length);
1598 virtual void didFinishLoading(blink::WebURLLoader* loader,
1599 double finish_time,
1600 int64_t total_encoded_data_length) {
1601 int32_t result = success_ ? PP_OK : PP_ERROR_FAILED;
1603 if (content::PepperPluginInstance::Get(instance_))
1604 stream_handler_->DidFinishStream(stream_handler_user_data_, result);
1605 delete this;
1608 virtual void didFail(blink::WebURLLoader* loader,
1609 const blink::WebURLError& error) {
1610 success_ = false;
1613 PP_Instance instance_;
1614 scoped_ptr<blink::WebURLLoader> url_loader_;
1615 std::string pexe_url_;
1616 int32_t pexe_opt_level_;
1617 const PPP_PexeStreamHandler* stream_handler_;
1618 void* stream_handler_user_data_;
1619 bool success_;
1620 int64_t expected_content_length_;
1621 base::WeakPtrFactory<PexeDownloader> weak_factory_;
1624 void StreamPexe(PP_Instance instance,
1625 const char* pexe_url,
1626 int32_t opt_level,
1627 const PPP_PexeStreamHandler* handler,
1628 void* handler_user_data) {
1629 content::PepperPluginInstance* plugin_instance =
1630 content::PepperPluginInstance::Get(instance);
1631 if (!plugin_instance) {
1632 base::MessageLoop::current()->PostTask(
1633 FROM_HERE,
1634 base::Bind(handler->DidFinishStream,
1635 handler_user_data,
1636 static_cast<int32_t>(PP_ERROR_FAILED)));
1637 return;
1640 GURL gurl(pexe_url);
1641 const blink::WebDocument& document =
1642 plugin_instance->GetContainer()->element().document();
1643 scoped_ptr<blink::WebURLLoader> url_loader(
1644 CreateWebURLLoader(document, gurl));
1645 PexeDownloader* downloader = new PexeDownloader(instance,
1646 url_loader.Pass(),
1647 pexe_url,
1648 opt_level,
1649 handler,
1650 handler_user_data);
1652 blink::WebURLRequest url_request = CreateWebURLRequest(document, gurl);
1653 // Mark the request as requesting a PNaCl bitcode file,
1654 // so that component updater can detect this user action.
1655 url_request.addHTTPHeaderField(
1656 blink::WebString::fromUTF8("Accept"),
1657 blink::WebString::fromUTF8("application/x-pnacl, */*"));
1658 url_request.setRequestContext(blink::WebURLRequest::RequestContextObject);
1659 downloader->Load(url_request);
1662 const PPB_NaCl_Private nacl_interface = {
1663 &LaunchSelLdr,
1664 &StartPpapiProxy,
1665 &UrandomFD,
1666 &Are3DInterfacesDisabled,
1667 &BrokerDuplicateHandle,
1668 &GetReadExecPnaclFd,
1669 &CreateTemporaryFile,
1670 &GetNumberOfProcessors,
1671 &PPIsNonSFIModeEnabled,
1672 &ReportTranslationFinished,
1673 &DispatchEvent,
1674 &ReportLoadSuccess,
1675 &ReportLoadError,
1676 &ReportLoadAbort,
1677 &InstanceCreated,
1678 &InstanceDestroyed,
1679 &NaClDebugEnabledForURL,
1680 &GetSandboxArch,
1681 &LogToConsole,
1682 &GetNaClReadyState,
1683 &Vlog,
1684 &InitializePlugin,
1685 &GetNexeSize,
1686 &RequestNaClManifest,
1687 &GetManifestBaseURL,
1688 &ProcessNaClManifest,
1689 &DevInterfacesEnabled,
1690 &ManifestGetProgramURL,
1691 &GetPNaClResourceInfo,
1692 &GetCpuFeatureAttrs,
1693 &DownloadNexe,
1694 &ReportSelLdrStatus,
1695 &LogTranslateTime,
1696 &OpenManifestEntry,
1697 &SetPNaClStartTime,
1698 &StreamPexe
1701 } // namespace
1703 const PPB_NaCl_Private* GetNaClPrivateInterface() {
1704 return &nacl_interface;
1707 } // namespace nacl