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