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