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