Upstream version 10.39.225.0
[platform/framework/web/crosswalk.git] / src / components / nacl / renderer / ppb_nacl_private_impl.cc
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.
4
5 #include "components/nacl/renderer/ppb_nacl_private_impl.h"
6
7 #include <numeric>
8 #include <string>
9 #include <vector>
10
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"
65
66 namespace nacl {
67 namespace {
68
69 // The pseudo-architecture used to indicate portable native client.
70 const char* const kPortableArch = "portable";
71
72 // The base URL for resources used by the PNaCl translator processes.
73 const char* kPNaClTranslatorBaseUrl = "chrome://pnacl-translator/";
74
75 base::LazyInstance<scoped_refptr<PnaclTranslationResourceHost> >
76     g_pnacl_resource_host = LAZY_INSTANCE_INITIALIZER;
77
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());
87   }
88   return true;
89 }
90
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;
98 };
99
100 typedef std::map<PP_Instance, InstanceInfo> InstanceInfoMap;
101
102 base::LazyInstance<InstanceInfoMap> g_instance_info =
103     LAZY_INSTANCE_INITIALIZER;
104
105 static const PP_NaClFileInfo kInvalidNaClFileInfo = {
106     PP_kInvalidFileHandle,
107     0,  // token_lo
108     0,  // token_hi
109 };
110
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);
119 }
120
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;
125   }
126
127 #if defined(OS_POSIX)
128   if (channel_handle.socket.fd == -1) {
129     return false;
130   }
131 #endif
132
133   return true;
134 }
135
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));
141 }
142
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);
148
149 typedef base::Callback<void(int32_t, const PP_NaClFileInfo&)>
150 DownloadFileCallback;
151
152 void DownloadFile(PP_Instance instance,
153                   const std::string& url,
154                   const DownloadFileCallback& callback);
155
156 PP_Bool StartPpapiProxy(PP_Instance instance);
157
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) {
166   }
167
168   virtual ~ManifestServiceProxy() { }
169
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);
185         }
186       }
187     }
188   }
189
190   virtual void OpenResource(
191       const std::string& key,
192       const ManifestServiceChannel::OpenResourceCallback& callback) OVERRIDE {
193     DCHECK(ppapi::PpapiGlobals::Get()->GetMainThreadMessageLoop()->
194                BelongsToCurrentThread());
195
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;
208     }
209
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.
214     //
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));
219   }
220
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;
229     }
230     callback.Run(base::File(file_info.handle),
231                  file_info.token_lo,
232                  file_info.token_hi);
233   }
234
235   PP_Instance pp_instance_;
236   DISALLOW_COPY_AND_ASSIGN(ManifestServiceProxy);
237 };
238
239 blink::WebURLLoader* CreateWebURLLoader(const blink::WebDocument& document,
240                                         const GURL& gurl) {
241   blink::WebURLLoaderOptions options;
242   options.untrustedHTTP = true;
243
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;
252   }
253   return document.frame()->createAssociatedURLLoader(options);
254 }
255
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;
263 }
264
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.
274   }
275   return PP_ERROR_FAILED;
276 }
277
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());
294
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));
299
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);
313       }
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;
319     }
320   }
321
322   InstanceInfo instance_info;
323   instance_info.url = GURL(alleged_url);
324
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;
335
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;
369   }
370
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);
378       }
379     }
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;
385   }
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;
390
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;
394
395   *(static_cast<NaClHandle*>(imc_handle)) = ToNativeHandle(result_socket);
396
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;
403   }
404
405   // Store the crash information shared memory handle.
406   load_manager->set_crash_info_shmem_handle(
407       launch_result.crash_info_shmem_handle);
408
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;
422   }
423
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);
446   }
447 }
448
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;
454
455   content::PepperPluginInstance* plugin_instance =
456       content::PepperPluginInstance::Get(instance);
457   if (!plugin_instance) {
458     DLOG(ERROR) << "GetInstance() failed";
459     return PP_FALSE;
460   }
461
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;
467   }
468   InstanceInfo instance_info = it->second;
469   map.erase(it);
470
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);
477
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.");
490   }
491   return PP_FALSE;
492 }
493
494 int UrandomFD(void) {
495 #if defined(OS_POSIX)
496   return base::GetUrandomFD();
497 #else
498   return -1;
499 #endif
500 }
501
502 PP_Bool Are3DInterfacesDisabled() {
503   return PP_FromBool(CommandLine::ForCurrentProcess()->HasSwitch(
504                          switches::kDisable3DAPIs));
505 }
506
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
519 }
520
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());
531
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);
539   }
540   return r;
541 }
542
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;
555   }
556   if (out_fd == IPC::InvalidPlatformFileForTransit()) {
557     return PP_kInvalidFileHandle;
558   }
559   return IPC::PlatformFileForTransitToPlatformFile(out_fd);
560 }
561
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);
568 }
569
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;
577   }
578
579   if (transit_fd == IPC::InvalidPlatformFileForTransit()) {
580     return PP_kInvalidFileHandle;
581   }
582
583   return IPC::PlatformFileForTransitToPlatformFile(transit_fd);
584 }
585
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;
592 }
593
594 PP_Bool PPIsNonSFIModeEnabled() {
595   return PP_FromBool(IsNonSFIModeEnabled());
596 }
597
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;
613   }
614
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();
626
627   g_pnacl_resource_host.Get()->RequestNexeFd(
628       GetRoutingID(instance),
629       instance,
630       cache_info,
631       callback);
632 }
633
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);
650
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());
660     }
661   }
662
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);
668 }
669
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;
678
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;
691
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;
705   }
706
707   if (out_fd == IPC::InvalidPlatformFileForTransit())
708     return PP_kInvalidFileHandle;
709
710   return IPC::PlatformFileForTransitToPlatformFile(out_fd);
711 }
712
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);
725 }
726
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);
735   }
736 }
737
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);
744 }
745
746 void ReportLoadAbort(PP_Instance instance) {
747   NexeLoadManager* load_manager = NexeLoadManager::Get(instance);
748   if (load_manager)
749     load_manager->ReportLoadAbort();
750 }
751
752 void InstanceCreated(PP_Instance instance) {
753   NexeLoadManager::Create(instance);
754 }
755
756 void InstanceDestroyed(PP_Instance instance) {
757   DeleteJsonManifest(instance);
758   NexeLoadManager::Delete(instance);
759 }
760
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);
771 }
772
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));
778 }
779
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;
786 }
787
788 void Vlog(const char* message) {
789   VLOG(1) << message;
790 }
791
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);
800 }
801
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;
808 }
809
810 void DownloadManifestToBuffer(PP_Instance instance,
811                               struct PP_CompletionCallback callback);
812
813 bool CreateJsonManifest(PP_Instance instance,
814                         const std::string& manifest_url,
815                         const std::string& manifest_data);
816
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;
827   }
828
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;
836   }
837
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.");
852       }
853     } else {
854       load_manager->ReportLoadError(PP_NACL_ERROR_MANIFEST_LOAD_URL,
855                                     "could not load manifest url.");
856     }
857     ppapi::PpapiGlobals::Get()->GetMainThreadMessageLoop()->PostTask(
858         FROM_HERE,
859         base::Bind(callback.func, callback.user_data, error));
860   } else {
861     DownloadManifestToBuffer(instance, callback);
862   }
863 }
864
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());
874 }
875
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);
880 }
881
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;
887 }
888
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);
894
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)));
906   }
907   const blink::WebDocument& document =
908       plugin_instance->GetContainer()->element().document();
909
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);
914
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);
922 }
923
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());
932
933   nacl::NexeLoadManager* load_manager = NexeLoadManager::Get(instance);
934   if (!load_manager) {
935     callback.func(callback.user_data, PP_ERROR_ABORTED);
936     return;
937   }
938
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.");
964   }
965
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;
970   }
971   callback.func(callback.user_data, pp_error);
972 }
973
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));
979
980   nacl::NexeLoadManager* load_manager = NexeLoadManager::Get(instance);
981   if (!load_manager)
982     return false;
983
984   const char* isa_type;
985   if (load_manager->IsPNaCl())
986     isa_type = kPortableArch;
987   else
988     isa_type = GetSandboxArch();
989
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;
1000   }
1001   load_manager->ReportLoadError(error_info.error, error_info.string);
1002   return false;
1003 }
1004
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);
1010
1011   JsonManifest* manifest = GetJsonManifest(instance);
1012   if (manifest == NULL)
1013     return PP_FALSE;
1014
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;
1023   }
1024
1025   if (load_manager)
1026     load_manager->ReportLoadError(error_info.error, error_info.string);
1027   return PP_FALSE;
1028 }
1029
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;
1047     }
1048     std::string key_basename = key.substr(kFilesPrefix.length());
1049     *full_url = std::string(kPNaClTranslatorBaseUrl) + GetSandboxArch() + "/" +
1050                 key_basename;
1051     return true;
1052   }
1053
1054   JsonManifest* manifest = GetJsonManifest(instance);
1055   if (manifest == NULL)
1056     return false;
1057
1058   return manifest->ResolveKey(key, full_url, pnacl_options);
1059 }
1060
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;
1069
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;
1081   }
1082
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;
1090   }
1091
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;
1097   }
1098
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;
1106   }
1107
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;
1114   }
1115
1116   // Null-terminate the bytes we we read from the file.
1117   buffer.get()[rc] = 0;
1118
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;
1128   }
1129
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;
1135   }
1136
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);
1142     }
1143   }
1144
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);
1150     }
1151   }
1152   return PP_TRUE;
1153 }
1154
1155 PP_Var GetCpuFeatureAttrs() {
1156   return ppapi::StringVar::StringToPPVar(GetCpuFeatures());
1157 }
1158
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;
1166 };
1167
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) { }
1174
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;
1187     }
1188   }
1189
1190  private:
1191   PP_Instance instance_;
1192   base::Time last_event_;
1193 };
1194
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);
1200
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();
1212
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;
1225   }
1226
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);
1231
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)));
1239   }
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);
1245
1246   ProgressEventRateLimiter* tracker = new ProgressEventRateLimiter(instance);
1247
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);
1256 }
1257
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;
1269   }
1270
1271   if (bytes_read == -1) {
1272     target_file.Close();
1273     pp_error = PP_ERROR_FAILED;
1274   }
1275
1276   base::TimeDelta download_time = base::Time::Now() - request.start_time;
1277
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);
1286   }
1287
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;
1292
1293   request.callback.func(request.callback.user_data, pp_error);
1294 }
1295
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;
1309   }
1310
1311   callback.Run(pp_error, file_info);
1312 }
1313
1314 void DownloadFile(PP_Instance instance,
1315                   const std::string& url,
1316                   const DownloadFileCallback& callback) {
1317   DCHECK(ppapi::PpapiGlobals::Get()->GetMainThreadMessageLoop()->
1318              BelongsToCurrentThread());
1319
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;
1329   }
1330
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;
1346     }
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;
1352   }
1353
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;
1364   }
1365
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;
1382   }
1383
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);
1388
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));
1397   }
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);
1403
1404   ProgressEventRateLimiter* tracker = new ProgressEventRateLimiter(instance);
1405
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);
1414 }
1415
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;
1424
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);
1430 }
1431
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));
1439 }
1440
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);
1448 }
1449
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);
1466   }
1467
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));
1472 }
1473
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());
1478 }
1479
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) { }
1499
1500   void Load(const blink::WebURLRequest& request) {
1501     url_loader_->loadAsynchronously(request, this);
1502   }
1503
1504  private:
1505   virtual void didReceiveResponse(blink::WebURLLoader* loader,
1506                                   const blink::WebURLResponse& response) {
1507     success_ = (response.httpStatusCode() == 200);
1508     if (!success_)
1509       return;
1510
1511     expected_content_length_ = response.expectedContentLength();
1512
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);
1516
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);
1522
1523     bool has_no_store_header = false;
1524     std::string cache_control =
1525         response.httpHeaderField("cache-control").utf8();
1526
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;
1534     }
1535
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()));
1544   }
1545
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;
1552     }
1553
1554     HistogramEnumerate("NaCl.Perf.PNaClCache.IsHit", cache_hit, 2);
1555     if (cache_hit) {
1556       stream_handler_->DidCacheHit(stream_handler_user_data_, file_handle);
1557
1558       // We delete the PexeDownloader at this point since we successfully got a
1559       // cached, translated nexe.
1560       delete this;
1561       return;
1562     }
1563     stream_handler_->DidCacheMiss(stream_handler_user_data_,
1564                                   expected_content_length_,
1565                                   file_handle);
1566
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);
1570   }
1571
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);
1581     }
1582   }
1583
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;
1588
1589     if (content::PepperPluginInstance::Get(instance_))
1590       stream_handler_->DidFinishStream(stream_handler_user_data_, result);
1591     delete this;
1592   }
1593
1594   virtual void didFail(blink::WebURLLoader* loader,
1595                        const blink::WebURLError& error) {
1596     success_ = false;
1597   }
1598
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_;
1608 };
1609
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;
1624   }
1625
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);
1637
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);
1646 }
1647
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
1685 };
1686
1687 }  // namespace
1688
1689 const PPB_NaCl_Private* GetNaClPrivateInterface() {
1690   return &nacl_interface;
1691 }
1692
1693 }  // namespace nacl