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