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