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