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