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