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