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
66namespace nacl {
67namespace {
68
69// The pseudo-architecture used to indicate portable native client.
70const char* const kPortableArch = "portable";
71
72// The base URL for resources used by the PNaCl translator processes.
73const char* kPNaClTranslatorBaseUrl = "chrome://pnacl-translator/";
74
75base::LazyInstance<scoped_refptr<PnaclTranslationResourceHost> >
76    g_pnacl_resource_host = LAZY_INSTANCE_INITIALIZER;
77
78bool 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
91struct 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
100typedef std::map<PP_Instance, InstanceInfo> InstanceInfoMap;
101
102base::LazyInstance<InstanceInfoMap> g_instance_info =
103    LAZY_INSTANCE_INITIALIZER;
104
105static const PP_NaClFileInfo kInvalidNaClFileInfo = {
106    PP_kInvalidFileHandle,
107    0,  // token_lo
108    0,  // token_hi
109};
110
111int 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.
122bool 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
136void 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
143bool 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
149typedef base::Callback<void(int32_t, const PP_NaClFileInfo&)>
150DownloadFileCallback;
151
152void DownloadFile(PP_Instance instance,
153                  const std::string& url,
154                  const DownloadFileCallback& callback);
155
156PP_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.
162class 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
239blink::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
256blink::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
265int32_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.
279void 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
449PP_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
494int UrandomFD(void) {
495#if defined(OS_POSIX)
496  return base::GetUrandomFD();
497#else
498  return -1;
499#endif
500}
501
502PP_Bool Are3DInterfacesDisabled() {
503  return PP_FromBool(CommandLine::ForCurrentProcess()->HasSwitch(
504                         switches::kDisable3DAPIs));
505}
506
507int32_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.
524std::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
543PP_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
562void 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
570PP_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
586int32_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
594PP_Bool PPIsNonSFIModeEnabled() {
595  return PP_FromBool(IsNonSFIModeEnabled());
596}
597
598void 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
634void 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
670PP_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
713void 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
727void 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
738void 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
746void ReportLoadAbort(PP_Instance instance) {
747  NexeLoadManager* load_manager = NexeLoadManager::Get(instance);
748  if (load_manager)
749    load_manager->ReportLoadAbort();
750}
751
752void InstanceCreated(PP_Instance instance) {
753  NexeLoadManager::Create(instance);
754}
755
756void InstanceDestroyed(PP_Instance instance) {
757  DeleteJsonManifest(instance);
758  NexeLoadManager::Delete(instance);
759}
760
761PP_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
773void 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
780PP_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
788void Vlog(const char* message) {
789  VLOG(1) << message;
790}
791
792void 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
802int64_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
810void DownloadManifestToBuffer(PP_Instance instance,
811                              struct PP_CompletionCallback callback);
812
813bool CreateJsonManifest(PP_Instance instance,
814                        const std::string& manifest_url,
815                        const std::string& manifest_data);
816
817void 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
865PP_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
876void 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
882PP_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
889void 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
895void 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
924void 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
974bool 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
1005PP_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
1030bool 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
1061PP_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
1155PP_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.
1161struct 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.
1170class 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
1195void DownloadNexeCompletion(const DownloadNexeRequest& request,
1196                            PP_NaClFileInfo* out_file_info,
1197                            FileDownloader::Status status,
1198                            base::File target_file,
1199                            int http_status);
1200
1201void 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
1258void 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
1296void 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
1314void 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
1416void 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
1432void 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
1441void 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
1450void 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
1474void 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.
1482class 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
1610void 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
1648const 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
1689const PPB_NaCl_Private* GetNaClPrivateInterface() {
1690  return &nacl_interface;
1691}
1692
1693}  // namespace nacl
1694