Revert of Fix doodle verification URL. (patchset #3 id:40001 of https://codereview...
[chromium-blink-merge.git] / components / nacl / renderer / ppb_nacl_private_impl.cc
blobedb4228e2c530e1a02820e2e3d11a0e7a7d9c7a1
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()), 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 // Launch NaCl's sel_ldr process.
279 void LaunchSelLdr(PP_Instance instance,
280 PP_Bool main_service_runtime,
281 const char* alleged_url,
282 const PP_NaClFileInfo* nexe_file_info,
283 PP_Bool uses_irt,
284 PP_Bool uses_ppapi,
285 PP_Bool uses_nonsfi_mode,
286 PP_Bool enable_ppapi_dev,
287 PP_Bool enable_dyncode_syscalls,
288 PP_Bool enable_exception_handling,
289 PP_Bool enable_crash_throttling,
290 void* imc_handle,
291 PP_CompletionCallback callback) {
292 CHECK(ppapi::PpapiGlobals::Get()->GetMainThreadMessageLoop()->
293 BelongsToCurrentThread());
295 // Create the manifest service proxy here, so on error case, it will be
296 // destructed (without passing it to ManifestServiceChannel).
297 scoped_ptr<ManifestServiceChannel::Delegate> manifest_service_proxy(
298 new ManifestServiceProxy(instance));
300 FileDescriptor result_socket;
301 IPC::Sender* sender = content::RenderThread::Get();
302 DCHECK(sender);
303 int routing_id = 0;
304 // If the nexe uses ppapi APIs, we need a routing ID.
305 // To get the routing ID, we must be on the main thread.
306 // Some nexes do not use ppapi and launch from the background thread,
307 // so those nexes can skip finding a routing_id.
308 if (uses_ppapi) {
309 routing_id = GetRoutingID(instance);
310 if (!routing_id) {
311 if (nexe_file_info->handle != PP_kInvalidFileHandle) {
312 base::File closer(nexe_file_info->handle);
314 ppapi::PpapiGlobals::Get()->GetMainThreadMessageLoop()->PostTask(
315 FROM_HERE,
316 base::Bind(callback.func, callback.user_data,
317 static_cast<int32_t>(PP_ERROR_FAILED)));
318 return;
322 InstanceInfo instance_info;
323 instance_info.url = GURL(alleged_url);
325 uint32_t perm_bits = ppapi::PERMISSION_NONE;
326 // Conditionally block 'Dev' interfaces. We do this for the NaCl process, so
327 // it's clearer to developers when they are using 'Dev' inappropriately. We
328 // must also check on the trusted side of the proxy.
329 if (enable_ppapi_dev)
330 perm_bits |= ppapi::PERMISSION_DEV;
331 instance_info.permissions =
332 ppapi::PpapiPermissions::GetForCommandLine(perm_bits);
333 std::string error_message_string;
334 NaClLaunchResult launch_result;
336 IPC::PlatformFileForTransit nexe_for_transit =
337 IPC::InvalidPlatformFileForTransit();
338 #if defined(OS_POSIX)
339 if (nexe_file_info->handle != PP_kInvalidFileHandle)
340 nexe_for_transit = base::FileDescriptor(nexe_file_info->handle, true);
341 #elif defined(OS_WIN)
342 // Duplicate the handle on the browser side instead of the renderer.
343 // This is because BrokerGetFileForProcess isn't part of content/public, and
344 // it's simpler to do the duplication in the browser anyway.
345 nexe_for_transit = nexe_file_info->handle;
346 #else
347 #error Unsupported target platform.
348 #endif
349 if (!sender->Send(new NaClHostMsg_LaunchNaCl(
350 NaClLaunchParams(
351 instance_info.url.spec(),
352 nexe_for_transit,
353 nexe_file_info->token_lo,
354 nexe_file_info->token_hi,
355 routing_id,
356 perm_bits,
357 PP_ToBool(uses_irt),
358 PP_ToBool(uses_nonsfi_mode),
359 PP_ToBool(enable_dyncode_syscalls),
360 PP_ToBool(enable_exception_handling),
361 PP_ToBool(enable_crash_throttling)),
362 &launch_result,
363 &error_message_string))) {
364 ppapi::PpapiGlobals::Get()->GetMainThreadMessageLoop()->PostTask(
365 FROM_HERE,
366 base::Bind(callback.func, callback.user_data,
367 static_cast<int32_t>(PP_ERROR_FAILED)));
368 return;
371 if (!error_message_string.empty()) {
372 if (PP_ToBool(main_service_runtime)) {
373 NexeLoadManager* load_manager = NexeLoadManager::Get(instance);
374 if (load_manager) {
375 load_manager->ReportLoadError(PP_NACL_ERROR_SEL_LDR_LAUNCH,
376 "ServiceRuntime: failed to start",
377 error_message_string);
380 ppapi::PpapiGlobals::Get()->GetMainThreadMessageLoop()->PostTask(
381 FROM_HERE,
382 base::Bind(callback.func, callback.user_data,
383 static_cast<int32_t>(PP_ERROR_FAILED)));
384 return;
386 result_socket = launch_result.imc_channel_handle;
387 instance_info.channel_handle = launch_result.ppapi_ipc_channel_handle;
388 instance_info.plugin_pid = launch_result.plugin_pid;
389 instance_info.plugin_child_id = launch_result.plugin_child_id;
391 // Don't save instance_info if channel handle is invalid.
392 if (IsValidChannelHandle(instance_info.channel_handle))
393 g_instance_info.Get()[instance] = instance_info;
395 *(static_cast<NaClHandle*>(imc_handle)) = ToNativeHandle(result_socket);
397 NexeLoadManager* load_manager = NexeLoadManager::Get(instance);
398 DCHECK(load_manager);
399 if (!load_manager) {
400 PostPPCompletionCallback(callback, PP_ERROR_FAILED);
401 base::SharedMemory::CloseHandle(launch_result.crash_info_shmem_handle);
402 return;
405 // Store the crash information shared memory handle.
406 load_manager->set_crash_info_shmem_handle(
407 launch_result.crash_info_shmem_handle);
409 // Create the trusted plugin channel.
410 if (IsValidChannelHandle(launch_result.trusted_ipc_channel_handle)) {
411 bool report_exit_status = PP_ToBool(main_service_runtime);
412 scoped_ptr<TrustedPluginChannel> trusted_plugin_channel(
413 new TrustedPluginChannel(
414 load_manager,
415 launch_result.trusted_ipc_channel_handle,
416 content::RenderThread::Get()->GetShutdownEvent(),
417 report_exit_status));
418 load_manager->set_trusted_plugin_channel(trusted_plugin_channel.Pass());
419 } else {
420 PostPPCompletionCallback(callback, PP_ERROR_FAILED);
421 return;
424 // Create the manifest service handle as well.
425 // For security hardening, disable the IPCs for open_resource() when they
426 // aren't needed. PNaCl doesn't expose open_resource(). Note that
427 // enable_dyncode_syscalls is true if and only if the plugin is a non-PNaCl
428 // plugin.
429 if (load_manager &&
430 enable_dyncode_syscalls &&
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 InstanceCreated(PP_Instance instance) {
753 NexeLoadManager::Create(instance);
756 void InstanceDestroyed(PP_Instance instance) {
757 DeleteJsonManifest(instance);
758 NexeLoadManager::Delete(instance);
761 PP_Bool NaClDebugEnabledForURL(const char* alleged_nmf_url) {
762 if (!CommandLine::ForCurrentProcess()->HasSwitch(switches::kEnableNaClDebug))
763 return PP_FALSE;
764 IPC::Sender* sender = content::RenderThread::Get();
765 DCHECK(sender);
766 bool should_debug = false;
767 return PP_FromBool(
768 sender->Send(new NaClHostMsg_NaClDebugEnabledForURL(GURL(alleged_nmf_url),
769 &should_debug)) &&
770 should_debug);
773 void LogToConsole(PP_Instance instance, const char* message) {
774 NexeLoadManager* load_manager = NexeLoadManager::Get(instance);
775 DCHECK(load_manager);
776 if (load_manager)
777 load_manager->LogToConsole(std::string(message));
780 PP_NaClReadyState GetNaClReadyState(PP_Instance instance) {
781 NexeLoadManager* load_manager = NexeLoadManager::Get(instance);
782 DCHECK(load_manager);
783 if (load_manager)
784 return load_manager->nacl_ready_state();
785 return PP_NACL_READY_STATE_UNSENT;
788 void Vlog(const char* message) {
789 VLOG(1) << message;
792 void InitializePlugin(PP_Instance instance,
793 uint32_t argc,
794 const char* argn[],
795 const char* argv[]) {
796 NexeLoadManager* load_manager = NexeLoadManager::Get(instance);
797 DCHECK(load_manager);
798 if (load_manager)
799 load_manager->InitializePlugin(argc, argn, argv);
802 int64_t GetNexeSize(PP_Instance instance) {
803 NexeLoadManager* load_manager = NexeLoadManager::Get(instance);
804 DCHECK(load_manager);
805 if (load_manager)
806 return load_manager->nexe_size();
807 return 0;
810 void DownloadManifestToBuffer(PP_Instance instance,
811 struct PP_CompletionCallback callback);
813 bool CreateJsonManifest(PP_Instance instance,
814 const std::string& manifest_url,
815 const std::string& manifest_data);
817 void RequestNaClManifest(PP_Instance instance,
818 PP_CompletionCallback callback) {
819 NexeLoadManager* load_manager = NexeLoadManager::Get(instance);
820 DCHECK(load_manager);
821 if (!load_manager) {
822 ppapi::PpapiGlobals::Get()->GetMainThreadMessageLoop()->PostTask(
823 FROM_HERE,
824 base::Bind(callback.func, callback.user_data,
825 static_cast<int32_t>(PP_ERROR_FAILED)));
826 return;
829 std::string url = load_manager->GetManifestURLArgument();
830 if (url.empty() || !load_manager->RequestNaClManifest(url)) {
831 ppapi::PpapiGlobals::Get()->GetMainThreadMessageLoop()->PostTask(
832 FROM_HERE,
833 base::Bind(callback.func, callback.user_data,
834 static_cast<int32_t>(PP_ERROR_FAILED)));
835 return;
838 const GURL& base_url = load_manager->manifest_base_url();
839 if (base_url.SchemeIs("data")) {
840 GURL gurl(base_url);
841 std::string mime_type;
842 std::string charset;
843 std::string data;
844 int32_t error = PP_ERROR_FAILED;
845 if (net::DataURL::Parse(gurl, &mime_type, &charset, &data)) {
846 if (data.size() <= ManifestDownloader::kNaClManifestMaxFileBytes) {
847 if (CreateJsonManifest(instance, base_url.spec(), data))
848 error = PP_OK;
849 } else {
850 load_manager->ReportLoadError(PP_NACL_ERROR_MANIFEST_TOO_LARGE,
851 "manifest file too large.");
853 } else {
854 load_manager->ReportLoadError(PP_NACL_ERROR_MANIFEST_LOAD_URL,
855 "could not load manifest url.");
857 ppapi::PpapiGlobals::Get()->GetMainThreadMessageLoop()->PostTask(
858 FROM_HERE,
859 base::Bind(callback.func, callback.user_data, error));
860 } else {
861 DownloadManifestToBuffer(instance, callback);
865 PP_Var GetManifestBaseURL(PP_Instance instance) {
866 NexeLoadManager* load_manager = NexeLoadManager::Get(instance);
867 DCHECK(load_manager);
868 if (!load_manager)
869 return PP_MakeUndefined();
870 const GURL& gurl = load_manager->manifest_base_url();
871 if (!gurl.is_valid())
872 return PP_MakeUndefined();
873 return ppapi::StringVar::StringToPPVar(gurl.spec());
876 void ProcessNaClManifest(PP_Instance instance, const char* program_url) {
877 nacl::NexeLoadManager* load_manager = NexeLoadManager::Get(instance);
878 if (load_manager)
879 load_manager->ProcessNaClManifest(program_url);
882 PP_Bool DevInterfacesEnabled(PP_Instance instance) {
883 nacl::NexeLoadManager* load_manager = NexeLoadManager::Get(instance);
884 if (load_manager)
885 return PP_FromBool(load_manager->DevInterfacesEnabled());
886 return PP_FALSE;
889 void DownloadManifestToBufferCompletion(PP_Instance instance,
890 struct PP_CompletionCallback callback,
891 base::Time start_time,
892 PP_NaClError pp_nacl_error,
893 const std::string& data);
895 void DownloadManifestToBuffer(PP_Instance instance,
896 struct PP_CompletionCallback callback) {
897 nacl::NexeLoadManager* load_manager = NexeLoadManager::Get(instance);
898 DCHECK(load_manager);
899 content::PepperPluginInstance* plugin_instance =
900 content::PepperPluginInstance::Get(instance);
901 if (!load_manager || !plugin_instance) {
902 ppapi::PpapiGlobals::Get()->GetMainThreadMessageLoop()->PostTask(
903 FROM_HERE,
904 base::Bind(callback.func, callback.user_data,
905 static_cast<int32_t>(PP_ERROR_FAILED)));
907 const blink::WebDocument& document =
908 plugin_instance->GetContainer()->element().document();
910 const GURL& gurl = load_manager->manifest_base_url();
911 scoped_ptr<blink::WebURLLoader> url_loader(
912 CreateWebURLLoader(document, gurl));
913 blink::WebURLRequest request = CreateWebURLRequest(document, gurl);
915 // ManifestDownloader deletes itself after invoking the callback.
916 ManifestDownloader* manifest_downloader = new ManifestDownloader(
917 url_loader.Pass(),
918 load_manager->is_installed(),
919 base::Bind(DownloadManifestToBufferCompletion,
920 instance, callback, base::Time::Now()));
921 manifest_downloader->Load(request);
924 void DownloadManifestToBufferCompletion(PP_Instance instance,
925 struct PP_CompletionCallback callback,
926 base::Time start_time,
927 PP_NaClError pp_nacl_error,
928 const std::string& data) {
929 base::TimeDelta download_time = base::Time::Now() - start_time;
930 HistogramTimeSmall("NaCl.Perf.StartupTime.ManifestDownload",
931 download_time.InMilliseconds());
933 nacl::NexeLoadManager* load_manager = NexeLoadManager::Get(instance);
934 if (!load_manager) {
935 callback.func(callback.user_data, PP_ERROR_ABORTED);
936 return;
939 int32_t pp_error;
940 switch (pp_nacl_error) {
941 case PP_NACL_ERROR_LOAD_SUCCESS:
942 pp_error = PP_OK;
943 break;
944 case PP_NACL_ERROR_MANIFEST_LOAD_URL:
945 pp_error = PP_ERROR_FAILED;
946 load_manager->ReportLoadError(PP_NACL_ERROR_MANIFEST_LOAD_URL,
947 "could not load manifest url.");
948 break;
949 case PP_NACL_ERROR_MANIFEST_TOO_LARGE:
950 pp_error = PP_ERROR_FILETOOBIG;
951 load_manager->ReportLoadError(PP_NACL_ERROR_MANIFEST_TOO_LARGE,
952 "manifest file too large.");
953 break;
954 case PP_NACL_ERROR_MANIFEST_NOACCESS_URL:
955 pp_error = PP_ERROR_NOACCESS;
956 load_manager->ReportLoadError(PP_NACL_ERROR_MANIFEST_NOACCESS_URL,
957 "access to manifest url was denied.");
958 break;
959 default:
960 NOTREACHED();
961 pp_error = PP_ERROR_FAILED;
962 load_manager->ReportLoadError(PP_NACL_ERROR_MANIFEST_LOAD_URL,
963 "could not load manifest url.");
966 if (pp_error == PP_OK) {
967 std::string base_url = load_manager->manifest_base_url().spec();
968 if (!CreateJsonManifest(instance, base_url, data))
969 pp_error = PP_ERROR_FAILED;
971 callback.func(callback.user_data, pp_error);
974 bool CreateJsonManifest(PP_Instance instance,
975 const std::string& manifest_url,
976 const std::string& manifest_data) {
977 HistogramSizeKB("NaCl.Perf.Size.Manifest",
978 static_cast<int32_t>(manifest_data.length() / 1024));
980 nacl::NexeLoadManager* load_manager = NexeLoadManager::Get(instance);
981 if (!load_manager)
982 return false;
984 const char* isa_type;
985 if (load_manager->IsPNaCl())
986 isa_type = kPortableArch;
987 else
988 isa_type = GetSandboxArch();
990 scoped_ptr<nacl::JsonManifest> j(
991 new nacl::JsonManifest(
992 manifest_url.c_str(),
993 isa_type,
994 IsNonSFIModeEnabled(),
995 PP_ToBool(NaClDebugEnabledForURL(manifest_url.c_str()))));
996 JsonManifest::ErrorInfo error_info;
997 if (j->Init(manifest_data.c_str(), &error_info)) {
998 AddJsonManifest(instance, j.Pass());
999 return true;
1001 load_manager->ReportLoadError(error_info.error, error_info.string);
1002 return false;
1005 PP_Bool ManifestGetProgramURL(PP_Instance instance,
1006 PP_Var* pp_full_url,
1007 PP_PNaClOptions* pnacl_options,
1008 PP_Bool* pp_uses_nonsfi_mode) {
1009 nacl::NexeLoadManager* load_manager = NexeLoadManager::Get(instance);
1011 JsonManifest* manifest = GetJsonManifest(instance);
1012 if (manifest == NULL)
1013 return PP_FALSE;
1015 bool uses_nonsfi_mode;
1016 std::string full_url;
1017 JsonManifest::ErrorInfo error_info;
1018 if (manifest->GetProgramURL(&full_url, pnacl_options, &uses_nonsfi_mode,
1019 &error_info)) {
1020 *pp_full_url = ppapi::StringVar::StringToPPVar(full_url);
1021 *pp_uses_nonsfi_mode = PP_FromBool(uses_nonsfi_mode);
1022 return PP_TRUE;
1025 if (load_manager)
1026 load_manager->ReportLoadError(error_info.error, error_info.string);
1027 return PP_FALSE;
1030 bool ManifestResolveKey(PP_Instance instance,
1031 bool is_helper_process,
1032 const std::string& key,
1033 std::string* full_url,
1034 PP_PNaClOptions* pnacl_options) {
1035 // For "helper" processes (llc and ld, for PNaCl translation), we resolve
1036 // keys manually as there is no existing .nmf file to parse.
1037 if (is_helper_process) {
1038 pnacl_options->translate = PP_FALSE;
1039 // We can only resolve keys in the files/ namespace.
1040 const std::string kFilesPrefix = "files/";
1041 if (key.find(kFilesPrefix) == std::string::npos) {
1042 nacl::NexeLoadManager* load_manager = NexeLoadManager::Get(instance);
1043 if (load_manager)
1044 load_manager->ReportLoadError(PP_NACL_ERROR_MANIFEST_RESOLVE_URL,
1045 "key did not start with files/");
1046 return false;
1048 std::string key_basename = key.substr(kFilesPrefix.length());
1049 *full_url = std::string(kPNaClTranslatorBaseUrl) + GetSandboxArch() + "/" +
1050 key_basename;
1051 return true;
1054 JsonManifest* manifest = GetJsonManifest(instance);
1055 if (manifest == NULL)
1056 return false;
1058 return manifest->ResolveKey(key, full_url, pnacl_options);
1061 PP_Bool GetPNaClResourceInfo(PP_Instance instance,
1062 PP_Var* llc_tool_name,
1063 PP_Var* ld_tool_name) {
1064 static const char kFilename[] = "chrome://pnacl-translator/pnacl.json";
1065 NexeLoadManager* load_manager = NexeLoadManager::Get(instance);
1066 DCHECK(load_manager);
1067 if (!load_manager)
1068 return PP_FALSE;
1070 uint64_t nonce_lo = 0;
1071 uint64_t nonce_hi = 0;
1072 base::File file(GetReadonlyPnaclFd(kFilename, false /* is_executable */,
1073 &nonce_lo, &nonce_hi));
1074 if (!file.IsValid()) {
1075 load_manager->ReportLoadError(
1076 PP_NACL_ERROR_PNACL_RESOURCE_FETCH,
1077 "The Portable Native Client (pnacl) component is not "
1078 "installed. Please consult chrome://components for more "
1079 "information.");
1080 return PP_FALSE;
1083 base::File::Info file_info;
1084 if (!file.GetInfo(&file_info)) {
1085 load_manager->ReportLoadError(
1086 PP_NACL_ERROR_PNACL_RESOURCE_FETCH,
1087 std::string("GetPNaClResourceInfo, GetFileInfo failed for: ") +
1088 kFilename);
1089 return PP_FALSE;
1092 if (file_info.size > 1 << 20) {
1093 load_manager->ReportLoadError(
1094 PP_NACL_ERROR_PNACL_RESOURCE_FETCH,
1095 std::string("GetPNaClResourceInfo, file too large: ") + kFilename);
1096 return PP_FALSE;
1099 scoped_ptr<char[]> buffer(new char[file_info.size + 1]);
1100 if (buffer.get() == NULL) {
1101 load_manager->ReportLoadError(
1102 PP_NACL_ERROR_PNACL_RESOURCE_FETCH,
1103 std::string("GetPNaClResourceInfo, couldn't allocate for: ") +
1104 kFilename);
1105 return PP_FALSE;
1108 int rc = file.Read(0, buffer.get(), file_info.size);
1109 if (rc < 0) {
1110 load_manager->ReportLoadError(
1111 PP_NACL_ERROR_PNACL_RESOURCE_FETCH,
1112 std::string("GetPNaClResourceInfo, reading failed for: ") + kFilename);
1113 return PP_FALSE;
1116 // Null-terminate the bytes we we read from the file.
1117 buffer.get()[rc] = 0;
1119 // Expect the JSON file to contain a top-level object (dictionary).
1120 Json::Reader json_reader;
1121 Json::Value json_data;
1122 if (!json_reader.parse(buffer.get(), json_data)) {
1123 load_manager->ReportLoadError(
1124 PP_NACL_ERROR_PNACL_RESOURCE_FETCH,
1125 std::string("Parsing resource info failed: JSON parse error: ") +
1126 json_reader.getFormattedErrorMessages());
1127 return PP_FALSE;
1130 if (!json_data.isObject()) {
1131 load_manager->ReportLoadError(
1132 PP_NACL_ERROR_PNACL_RESOURCE_FETCH,
1133 "Parsing resource info failed: Malformed JSON dictionary");
1134 return PP_FALSE;
1137 if (json_data.isMember("pnacl-llc-name")) {
1138 Json::Value json_name = json_data["pnacl-llc-name"];
1139 if (json_name.isString()) {
1140 std::string llc_tool_name_str = json_name.asString();
1141 *llc_tool_name = ppapi::StringVar::StringToPPVar(llc_tool_name_str);
1145 if (json_data.isMember("pnacl-ld-name")) {
1146 Json::Value json_name = json_data["pnacl-ld-name"];
1147 if (json_name.isString()) {
1148 std::string ld_tool_name_str = json_name.asString();
1149 *ld_tool_name = ppapi::StringVar::StringToPPVar(ld_tool_name_str);
1152 return PP_TRUE;
1155 PP_Var GetCpuFeatureAttrs() {
1156 return ppapi::StringVar::StringToPPVar(GetCpuFeatures());
1159 // Encapsulates some of the state for a call to DownloadNexe to prevent
1160 // argument lists from getting too long.
1161 struct DownloadNexeRequest {
1162 PP_Instance instance;
1163 std::string url;
1164 PP_CompletionCallback callback;
1165 base::Time start_time;
1168 // A utility class to ensure that we don't send progress events more often than
1169 // every 10ms for a given file.
1170 class ProgressEventRateLimiter {
1171 public:
1172 explicit ProgressEventRateLimiter(PP_Instance instance)
1173 : instance_(instance) { }
1175 void ReportProgress(const std::string& url,
1176 int64_t total_bytes_received,
1177 int64_t total_bytes_to_be_received) {
1178 base::Time now = base::Time::Now();
1179 if (now - last_event_ > base::TimeDelta::FromMilliseconds(10)) {
1180 DispatchProgressEvent(instance_,
1181 ProgressEvent(PP_NACL_EVENT_PROGRESS,
1182 url,
1183 total_bytes_to_be_received >= 0,
1184 total_bytes_received,
1185 total_bytes_to_be_received));
1186 last_event_ = now;
1190 private:
1191 PP_Instance instance_;
1192 base::Time last_event_;
1195 void DownloadNexeCompletion(const DownloadNexeRequest& request,
1196 PP_NaClFileInfo* out_file_info,
1197 FileDownloader::Status status,
1198 base::File target_file,
1199 int http_status);
1201 void DownloadNexe(PP_Instance instance,
1202 const char* url,
1203 PP_NaClFileInfo* out_file_info,
1204 PP_CompletionCallback callback) {
1205 CHECK(url);
1206 CHECK(out_file_info);
1207 DownloadNexeRequest request;
1208 request.instance = instance;
1209 request.url = url;
1210 request.callback = callback;
1211 request.start_time = base::Time::Now();
1213 // Try the fast path for retrieving the file first.
1214 PP_FileHandle handle = OpenNaClExecutable(instance,
1215 url,
1216 &out_file_info->token_lo,
1217 &out_file_info->token_hi);
1218 if (handle != PP_kInvalidFileHandle) {
1219 DownloadNexeCompletion(request,
1220 out_file_info,
1221 FileDownloader::SUCCESS,
1222 base::File(handle),
1223 200);
1224 return;
1227 // The fast path didn't work, we'll fetch the file using URLLoader and write
1228 // it to local storage.
1229 base::File target_file(CreateTemporaryFile(instance));
1230 GURL gurl(url);
1232 content::PepperPluginInstance* plugin_instance =
1233 content::PepperPluginInstance::Get(instance);
1234 if (!plugin_instance) {
1235 ppapi::PpapiGlobals::Get()->GetMainThreadMessageLoop()->PostTask(
1236 FROM_HERE,
1237 base::Bind(callback.func, callback.user_data,
1238 static_cast<int32_t>(PP_ERROR_FAILED)));
1240 const blink::WebDocument& document =
1241 plugin_instance->GetContainer()->element().document();
1242 scoped_ptr<blink::WebURLLoader> url_loader(
1243 CreateWebURLLoader(document, gurl));
1244 blink::WebURLRequest url_request = CreateWebURLRequest(document, gurl);
1246 ProgressEventRateLimiter* tracker = new ProgressEventRateLimiter(instance);
1248 // FileDownloader deletes itself after invoking DownloadNexeCompletion.
1249 FileDownloader* file_downloader = new FileDownloader(
1250 url_loader.Pass(),
1251 target_file.Pass(),
1252 base::Bind(&DownloadNexeCompletion, request, out_file_info),
1253 base::Bind(&ProgressEventRateLimiter::ReportProgress,
1254 base::Owned(tracker), std::string(url)));
1255 file_downloader->Load(url_request);
1258 void DownloadNexeCompletion(const DownloadNexeRequest& request,
1259 PP_NaClFileInfo* out_file_info,
1260 FileDownloader::Status status,
1261 base::File target_file,
1262 int http_status) {
1263 int32_t pp_error = FileDownloaderToPepperError(status);
1264 int64_t bytes_read = -1;
1265 if (pp_error == PP_OK && target_file.IsValid()) {
1266 base::File::Info info;
1267 if (target_file.GetInfo(&info))
1268 bytes_read = info.size;
1271 if (bytes_read == -1) {
1272 target_file.Close();
1273 pp_error = PP_ERROR_FAILED;
1276 base::TimeDelta download_time = base::Time::Now() - request.start_time;
1278 NexeLoadManager* load_manager = NexeLoadManager::Get(request.instance);
1279 if (load_manager) {
1280 load_manager->NexeFileDidOpen(pp_error,
1281 target_file,
1282 http_status,
1283 bytes_read,
1284 request.url,
1285 download_time);
1288 if (pp_error == PP_OK && target_file.IsValid())
1289 out_file_info->handle = target_file.TakePlatformFile();
1290 else
1291 out_file_info->handle = PP_kInvalidFileHandle;
1293 request.callback.func(request.callback.user_data, pp_error);
1296 void DownloadFileCompletion(
1297 const DownloadFileCallback& callback,
1298 FileDownloader::Status status,
1299 base::File file,
1300 int http_status) {
1301 int32_t pp_error = FileDownloaderToPepperError(status);
1302 PP_NaClFileInfo file_info;
1303 if (pp_error == PP_OK) {
1304 file_info.handle = file.TakePlatformFile();
1305 file_info.token_lo = 0;
1306 file_info.token_hi = 0;
1307 } else {
1308 file_info = kInvalidNaClFileInfo;
1311 callback.Run(pp_error, file_info);
1314 void DownloadFile(PP_Instance instance,
1315 const std::string& url,
1316 const DownloadFileCallback& callback) {
1317 DCHECK(ppapi::PpapiGlobals::Get()->GetMainThreadMessageLoop()->
1318 BelongsToCurrentThread());
1320 NexeLoadManager* load_manager = NexeLoadManager::Get(instance);
1321 DCHECK(load_manager);
1322 if (!load_manager) {
1323 base::MessageLoop::current()->PostTask(
1324 FROM_HERE,
1325 base::Bind(callback,
1326 static_cast<int32_t>(PP_ERROR_FAILED),
1327 kInvalidNaClFileInfo));
1328 return;
1331 // Handle special PNaCl support files which are installed on the user's
1332 // machine.
1333 if (url.find(kPNaClTranslatorBaseUrl, 0) == 0) {
1334 PP_NaClFileInfo file_info = kInvalidNaClFileInfo;
1335 PP_FileHandle handle = GetReadonlyPnaclFd(url.c_str(),
1336 false /* is_executable */,
1337 &file_info.token_lo,
1338 &file_info.token_hi);
1339 if (handle == PP_kInvalidFileHandle) {
1340 base::MessageLoop::current()->PostTask(
1341 FROM_HERE,
1342 base::Bind(callback,
1343 static_cast<int32_t>(PP_ERROR_FAILED),
1344 kInvalidNaClFileInfo));
1345 return;
1347 file_info.handle = handle;
1348 base::MessageLoop::current()->PostTask(
1349 FROM_HERE,
1350 base::Bind(callback, static_cast<int32_t>(PP_OK), file_info));
1351 return;
1354 // We have to ensure that this url resolves relative to the plugin base url
1355 // before downloading it.
1356 const GURL& test_gurl = load_manager->plugin_base_url().Resolve(url);
1357 if (!test_gurl.is_valid()) {
1358 base::MessageLoop::current()->PostTask(
1359 FROM_HERE,
1360 base::Bind(callback,
1361 static_cast<int32_t>(PP_ERROR_FAILED),
1362 kInvalidNaClFileInfo));
1363 return;
1366 // Try the fast path for retrieving the file first.
1367 uint64_t file_token_lo = 0;
1368 uint64_t file_token_hi = 0;
1369 PP_FileHandle file_handle = OpenNaClExecutable(instance,
1370 url.c_str(),
1371 &file_token_lo,
1372 &file_token_hi);
1373 if (file_handle != PP_kInvalidFileHandle) {
1374 PP_NaClFileInfo file_info;
1375 file_info.handle = file_handle;
1376 file_info.token_lo = file_token_lo;
1377 file_info.token_hi = file_token_hi;
1378 base::MessageLoop::current()->PostTask(
1379 FROM_HERE,
1380 base::Bind(callback, static_cast<int32_t>(PP_OK), file_info));
1381 return;
1384 // The fast path didn't work, we'll fetch the file using URLLoader and write
1385 // it to local storage.
1386 base::File target_file(CreateTemporaryFile(instance));
1387 GURL gurl(url);
1389 content::PepperPluginInstance* plugin_instance =
1390 content::PepperPluginInstance::Get(instance);
1391 if (!plugin_instance) {
1392 base::MessageLoop::current()->PostTask(
1393 FROM_HERE,
1394 base::Bind(callback,
1395 static_cast<int32_t>(PP_ERROR_FAILED),
1396 kInvalidNaClFileInfo));
1398 const blink::WebDocument& document =
1399 plugin_instance->GetContainer()->element().document();
1400 scoped_ptr<blink::WebURLLoader> url_loader(
1401 CreateWebURLLoader(document, gurl));
1402 blink::WebURLRequest url_request = CreateWebURLRequest(document, gurl);
1404 ProgressEventRateLimiter* tracker = new ProgressEventRateLimiter(instance);
1406 // FileDownloader deletes itself after invoking DownloadNexeCompletion.
1407 FileDownloader* file_downloader = new FileDownloader(
1408 url_loader.Pass(),
1409 target_file.Pass(),
1410 base::Bind(&DownloadFileCompletion, callback),
1411 base::Bind(&ProgressEventRateLimiter::ReportProgress,
1412 base::Owned(tracker), std::string(url)));
1413 file_downloader->Load(url_request);
1416 void ReportSelLdrStatus(PP_Instance instance,
1417 int32_t load_status,
1418 int32_t max_status) {
1419 HistogramEnumerate("NaCl.LoadStatus.SelLdr", load_status, max_status);
1420 NexeLoadManager* load_manager = NexeLoadManager::Get(instance);
1421 DCHECK(load_manager);
1422 if (!load_manager)
1423 return;
1425 // Gather data to see if being installed changes load outcomes.
1426 const char* name = load_manager->is_installed() ?
1427 "NaCl.LoadStatus.SelLdr.InstalledApp" :
1428 "NaCl.LoadStatus.SelLdr.NotInstalledApp";
1429 HistogramEnumerate(name, load_status, max_status);
1432 void LogTranslateTime(const char* histogram_name,
1433 int64_t time_in_us) {
1434 ppapi::PpapiGlobals::Get()->GetMainThreadMessageLoop()->PostTask(
1435 FROM_HERE,
1436 base::Bind(&HistogramTimeTranslation,
1437 std::string(histogram_name),
1438 time_in_us / 1000));
1441 void DidOpenManifestEntry(PP_NaClFileInfo* out_file_info,
1442 PP_CompletionCallback callback,
1443 int32_t pp_error,
1444 const PP_NaClFileInfo& file_info) {
1445 if (pp_error == PP_OK)
1446 *out_file_info = file_info;
1447 callback.func(callback.user_data, pp_error);
1450 void OpenManifestEntry(PP_Instance instance,
1451 PP_Bool is_helper_process,
1452 const char* key,
1453 PP_NaClFileInfo* out_file_info,
1454 PP_CompletionCallback callback) {
1455 std::string url;
1456 PP_PNaClOptions pnacl_options;
1457 pnacl_options.translate = PP_FALSE;
1458 pnacl_options.is_debug = PP_FALSE;
1459 pnacl_options.opt_level = 2;
1460 if (!ManifestResolveKey(instance,
1461 PP_ToBool(is_helper_process),
1462 key,
1463 &url,
1464 &pnacl_options)) {
1465 PostPPCompletionCallback(callback, PP_ERROR_FAILED);
1468 // TODO(teravest): Make a type like PP_NaClFileInfo to use for DownloadFile
1469 // that would close the file handle on destruction.
1470 DownloadFile(instance, url,
1471 base::Bind(&DidOpenManifestEntry, out_file_info, callback));
1474 void SetPNaClStartTime(PP_Instance instance) {
1475 NexeLoadManager* load_manager = NexeLoadManager::Get(instance);
1476 if (load_manager)
1477 load_manager->set_pnacl_start_time(base::Time::Now());
1480 // PexeDownloader is responsible for deleting itself when the download
1481 // finishes.
1482 class PexeDownloader : public blink::WebURLLoaderClient {
1483 public:
1484 PexeDownloader(PP_Instance instance,
1485 scoped_ptr<blink::WebURLLoader> url_loader,
1486 const std::string& pexe_url,
1487 int32_t pexe_opt_level,
1488 const PPP_PexeStreamHandler* stream_handler,
1489 void* stream_handler_user_data)
1490 : instance_(instance),
1491 url_loader_(url_loader.Pass()),
1492 pexe_url_(pexe_url),
1493 pexe_opt_level_(pexe_opt_level),
1494 stream_handler_(stream_handler),
1495 stream_handler_user_data_(stream_handler_user_data),
1496 success_(false),
1497 expected_content_length_(-1),
1498 weak_factory_(this) { }
1500 void Load(const blink::WebURLRequest& request) {
1501 url_loader_->loadAsynchronously(request, this);
1504 private:
1505 virtual void didReceiveResponse(blink::WebURLLoader* loader,
1506 const blink::WebURLResponse& response) {
1507 success_ = (response.httpStatusCode() == 200);
1508 if (!success_)
1509 return;
1511 expected_content_length_ = response.expectedContentLength();
1513 // Defer loading after receiving headers. This is because we may already
1514 // have a cached translated nexe, so check for that now.
1515 url_loader_->setDefersLoading(true);
1517 std::string etag = response.httpHeaderField("etag").utf8();
1518 std::string last_modified =
1519 response.httpHeaderField("last-modified").utf8();
1520 base::Time last_modified_time;
1521 base::Time::FromString(last_modified.c_str(), &last_modified_time);
1523 bool has_no_store_header = false;
1524 std::string cache_control =
1525 response.httpHeaderField("cache-control").utf8();
1527 std::vector<std::string> values;
1528 base::SplitString(cache_control, ',', &values);
1529 for (std::vector<std::string>::const_iterator it = values.begin();
1530 it != values.end();
1531 ++it) {
1532 if (base::StringToLowerASCII(*it) == "no-store")
1533 has_no_store_header = true;
1536 GetNexeFd(instance_,
1537 pexe_url_,
1538 pexe_opt_level_,
1539 last_modified_time,
1540 etag,
1541 has_no_store_header,
1542 base::Bind(&PexeDownloader::didGetNexeFd,
1543 weak_factory_.GetWeakPtr()));
1546 virtual void didGetNexeFd(int32_t pp_error,
1547 bool cache_hit,
1548 PP_FileHandle file_handle) {
1549 if (!content::PepperPluginInstance::Get(instance_)) {
1550 delete this;
1551 return;
1554 HistogramEnumerate("NaCl.Perf.PNaClCache.IsHit", cache_hit, 2);
1555 if (cache_hit) {
1556 stream_handler_->DidCacheHit(stream_handler_user_data_, file_handle);
1558 // We delete the PexeDownloader at this point since we successfully got a
1559 // cached, translated nexe.
1560 delete this;
1561 return;
1563 stream_handler_->DidCacheMiss(stream_handler_user_data_,
1564 expected_content_length_,
1565 file_handle);
1567 // No translated nexe was found in the cache, so we should download the
1568 // file to start streaming it.
1569 url_loader_->setDefersLoading(false);
1572 virtual void didReceiveData(blink::WebURLLoader* loader,
1573 const char* data,
1574 int data_length,
1575 int encoded_data_length) {
1576 if (content::PepperPluginInstance::Get(instance_)) {
1577 // Stream the data we received to the stream callback.
1578 stream_handler_->DidStreamData(stream_handler_user_data_,
1579 data,
1580 data_length);
1584 virtual void didFinishLoading(blink::WebURLLoader* loader,
1585 double finish_time,
1586 int64_t total_encoded_data_length) {
1587 int32_t result = success_ ? PP_OK : PP_ERROR_FAILED;
1589 if (content::PepperPluginInstance::Get(instance_))
1590 stream_handler_->DidFinishStream(stream_handler_user_data_, result);
1591 delete this;
1594 virtual void didFail(blink::WebURLLoader* loader,
1595 const blink::WebURLError& error) {
1596 success_ = false;
1599 PP_Instance instance_;
1600 scoped_ptr<blink::WebURLLoader> url_loader_;
1601 std::string pexe_url_;
1602 int32_t pexe_opt_level_;
1603 const PPP_PexeStreamHandler* stream_handler_;
1604 void* stream_handler_user_data_;
1605 bool success_;
1606 int64_t expected_content_length_;
1607 base::WeakPtrFactory<PexeDownloader> weak_factory_;
1610 void StreamPexe(PP_Instance instance,
1611 const char* pexe_url,
1612 int32_t opt_level,
1613 const PPP_PexeStreamHandler* handler,
1614 void* handler_user_data) {
1615 content::PepperPluginInstance* plugin_instance =
1616 content::PepperPluginInstance::Get(instance);
1617 if (!plugin_instance) {
1618 base::MessageLoop::current()->PostTask(
1619 FROM_HERE,
1620 base::Bind(handler->DidFinishStream,
1621 handler_user_data,
1622 static_cast<int32_t>(PP_ERROR_FAILED)));
1623 return;
1626 GURL gurl(pexe_url);
1627 const blink::WebDocument& document =
1628 plugin_instance->GetContainer()->element().document();
1629 scoped_ptr<blink::WebURLLoader> url_loader(
1630 CreateWebURLLoader(document, gurl));
1631 PexeDownloader* downloader = new PexeDownloader(instance,
1632 url_loader.Pass(),
1633 pexe_url,
1634 opt_level,
1635 handler,
1636 handler_user_data);
1638 blink::WebURLRequest url_request = CreateWebURLRequest(document, gurl);
1639 // Mark the request as requesting a PNaCl bitcode file,
1640 // so that component updater can detect this user action.
1641 url_request.addHTTPHeaderField(
1642 blink::WebString::fromUTF8("Accept"),
1643 blink::WebString::fromUTF8("application/x-pnacl, */*"));
1644 url_request.setRequestContext(blink::WebURLRequest::RequestContextObject);
1645 downloader->Load(url_request);
1648 const PPB_NaCl_Private nacl_interface = {
1649 &LaunchSelLdr,
1650 &StartPpapiProxy,
1651 &UrandomFD,
1652 &Are3DInterfacesDisabled,
1653 &BrokerDuplicateHandle,
1654 &GetReadExecPnaclFd,
1655 &CreateTemporaryFile,
1656 &GetNumberOfProcessors,
1657 &PPIsNonSFIModeEnabled,
1658 &ReportTranslationFinished,
1659 &DispatchEvent,
1660 &ReportLoadSuccess,
1661 &ReportLoadError,
1662 &ReportLoadAbort,
1663 &InstanceCreated,
1664 &InstanceDestroyed,
1665 &NaClDebugEnabledForURL,
1666 &GetSandboxArch,
1667 &LogToConsole,
1668 &GetNaClReadyState,
1669 &Vlog,
1670 &InitializePlugin,
1671 &GetNexeSize,
1672 &RequestNaClManifest,
1673 &GetManifestBaseURL,
1674 &ProcessNaClManifest,
1675 &DevInterfacesEnabled,
1676 &ManifestGetProgramURL,
1677 &GetPNaClResourceInfo,
1678 &GetCpuFeatureAttrs,
1679 &DownloadNexe,
1680 &ReportSelLdrStatus,
1681 &LogTranslateTime,
1682 &OpenManifestEntry,
1683 &SetPNaClStartTime,
1684 &StreamPexe
1687 } // namespace
1689 const PPB_NaCl_Private* GetNaClPrivateInterface() {
1690 return &nacl_interface;
1693 } // namespace nacl