Don't preload rarely seen large images
[chromium-blink-merge.git] / components / nacl / renderer / ppb_nacl_private_impl.cc
blobf407aa464fff7a963d1fd770c81354700bcb20ee
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.
5 #include "components/nacl/renderer/ppb_nacl_private_impl.h"
7 #include <numeric>
8 #include <string>
9 #include <vector>
11 #include "base/bind.h"
12 #include "base/bind_helpers.h"
13 #include "base/command_line.h"
14 #include "base/containers/scoped_ptr_hash_map.h"
15 #include "base/cpu.h"
16 #include "base/files/file.h"
17 #include "base/json/json_reader.h"
18 #include "base/lazy_instance.h"
19 #include "base/location.h"
20 #include "base/logging.h"
21 #include "base/rand_util.h"
22 #include "base/single_thread_task_runner.h"
23 #include "base/strings/string_split.h"
24 #include "base/strings/string_util.h"
25 #include "base/thread_task_runner_handle.h"
26 #include "components/nacl/common/nacl_host_messages.h"
27 #include "components/nacl/common/nacl_messages.h"
28 #include "components/nacl/common/nacl_nonsfi_util.h"
29 #include "components/nacl/common/nacl_switches.h"
30 #include "components/nacl/common/nacl_types.h"
31 #include "components/nacl/renderer/file_downloader.h"
32 #include "components/nacl/renderer/histogram.h"
33 #include "components/nacl/renderer/json_manifest.h"
34 #include "components/nacl/renderer/manifest_downloader.h"
35 #include "components/nacl/renderer/manifest_service_channel.h"
36 #include "components/nacl/renderer/nexe_load_manager.h"
37 #include "components/nacl/renderer/platform_info.h"
38 #include "components/nacl/renderer/pnacl_translation_resource_host.h"
39 #include "components/nacl/renderer/progress_event.h"
40 #include "components/nacl/renderer/trusted_plugin_channel.h"
41 #include "content/public/common/content_client.h"
42 #include "content/public/common/content_switches.h"
43 #include "content/public/common/sandbox_init.h"
44 #include "content/public/renderer/pepper_plugin_instance.h"
45 #include "content/public/renderer/render_thread.h"
46 #include "content/public/renderer/render_view.h"
47 #include "content/public/renderer/renderer_ppapi_host.h"
48 #include "native_client/src/public/imc_types.h"
49 #include "net/base/data_url.h"
50 #include "net/base/net_errors.h"
51 #include "net/http/http_util.h"
52 #include "ppapi/c/pp_bool.h"
53 #include "ppapi/c/private/pp_file_handle.h"
54 #include "ppapi/shared_impl/ppapi_globals.h"
55 #include "ppapi/shared_impl/ppapi_permissions.h"
56 #include "ppapi/shared_impl/ppapi_preferences.h"
57 #include "ppapi/shared_impl/var.h"
58 #include "ppapi/shared_impl/var_tracker.h"
59 #include "ppapi/thunk/enter.h"
60 #include "third_party/WebKit/public/platform/WebURLLoader.h"
61 #include "third_party/WebKit/public/platform/WebURLResponse.h"
62 #include "third_party/WebKit/public/web/WebDocument.h"
63 #include "third_party/WebKit/public/web/WebElement.h"
64 #include "third_party/WebKit/public/web/WebLocalFrame.h"
65 #include "third_party/WebKit/public/web/WebPluginContainer.h"
66 #include "third_party/WebKit/public/web/WebSecurityOrigin.h"
67 #include "third_party/WebKit/public/web/WebURLLoaderOptions.h"
69 namespace nacl {
70 namespace {
72 // The pseudo-architecture used to indicate portable native client.
73 const char* const kPortableArch = "portable";
75 // The base URL for resources used by the PNaCl translator processes.
76 const char* kPNaClTranslatorBaseUrl = "chrome://pnacl-translator/";
78 base::LazyInstance<scoped_refptr<PnaclTranslationResourceHost> >
79 g_pnacl_resource_host = LAZY_INSTANCE_INITIALIZER;
81 bool InitializePnaclResourceHost() {
82 // Must run on the main thread.
83 content::RenderThread* render_thread = content::RenderThread::Get();
84 if (!render_thread)
85 return false;
86 if (!g_pnacl_resource_host.Get().get()) {
87 g_pnacl_resource_host.Get() = new PnaclTranslationResourceHost(
88 render_thread->GetIOMessageLoopProxy());
89 render_thread->AddFilter(g_pnacl_resource_host.Get().get());
91 return true;
94 bool CanOpenViaFastPath(content::PepperPluginInstance* plugin_instance,
95 const GURL& gurl) {
96 // Fast path only works for installed file URLs.
97 if (!gurl.SchemeIs("chrome-extension"))
98 return PP_kInvalidFileHandle;
100 // IMPORTANT: Make sure the document can request the given URL. If we don't
101 // check, a malicious app could probe the extension system. This enforces a
102 // same-origin policy which prevents the app from requesting resources from
103 // another app.
104 blink::WebSecurityOrigin security_origin =
105 plugin_instance->GetContainer()->element().document().securityOrigin();
106 return security_origin.canRequest(gurl);
109 // This contains state that is produced by LaunchSelLdr() and consumed
110 // by StartPpapiProxy().
111 struct InstanceInfo {
112 InstanceInfo() : plugin_pid(base::kNullProcessId), plugin_child_id(0) {}
113 GURL url;
114 ppapi::PpapiPermissions permissions;
115 base::ProcessId plugin_pid;
116 int plugin_child_id;
117 IPC::ChannelHandle channel_handle;
120 class NaClPluginInstance {
121 public:
122 NaClPluginInstance(PP_Instance instance):
123 nexe_load_manager(instance), pexe_size(0) {}
125 NexeLoadManager nexe_load_manager;
126 scoped_ptr<JsonManifest> json_manifest;
127 scoped_ptr<InstanceInfo> instance_info;
129 // When translation is complete, this records the size of the pexe in
130 // bytes so that it can be reported in a later load event.
131 uint64_t pexe_size;
134 typedef base::ScopedPtrHashMap<PP_Instance, scoped_ptr<NaClPluginInstance>>
135 InstanceMap;
136 base::LazyInstance<InstanceMap> g_instance_map = LAZY_INSTANCE_INITIALIZER;
138 NaClPluginInstance* GetNaClPluginInstance(PP_Instance instance) {
139 InstanceMap& map = g_instance_map.Get();
140 InstanceMap::iterator iter = map.find(instance);
141 if (iter == map.end())
142 return NULL;
143 return iter->second;
146 NexeLoadManager* GetNexeLoadManager(PP_Instance instance) {
147 NaClPluginInstance* nacl_plugin_instance = GetNaClPluginInstance(instance);
148 if (!nacl_plugin_instance)
149 return NULL;
150 return &nacl_plugin_instance->nexe_load_manager;
153 JsonManifest* GetJsonManifest(PP_Instance instance) {
154 NaClPluginInstance* nacl_plugin_instance = GetNaClPluginInstance(instance);
155 if (!nacl_plugin_instance)
156 return NULL;
157 return nacl_plugin_instance->json_manifest.get();
160 static const PP_NaClFileInfo kInvalidNaClFileInfo = {
161 PP_kInvalidFileHandle,
162 0, // token_lo
163 0, // token_hi
166 int GetRoutingID(PP_Instance instance) {
167 // Check that we are on the main renderer thread.
168 DCHECK(content::RenderThread::Get());
169 content::RendererPpapiHost* host =
170 content::RendererPpapiHost::GetForPPInstance(instance);
171 if (!host)
172 return 0;
173 return host->GetRoutingIDForWidget(instance);
176 // Returns whether the channel_handle is valid or not.
177 bool IsValidChannelHandle(const IPC::ChannelHandle& channel_handle) {
178 if (channel_handle.name.empty()) {
179 return false;
182 #if defined(OS_POSIX)
183 if (channel_handle.socket.fd == -1) {
184 return false;
186 #endif
188 return true;
191 void PostPPCompletionCallback(PP_CompletionCallback callback,
192 int32_t status) {
193 ppapi::PpapiGlobals::Get()->GetMainThreadMessageLoop()->PostTask(
194 FROM_HERE,
195 base::Bind(callback.func, callback.user_data, status));
198 bool ManifestResolveKey(PP_Instance instance,
199 bool is_helper_process,
200 const std::string& key,
201 std::string* full_url,
202 PP_PNaClOptions* pnacl_options);
204 typedef base::Callback<void(int32_t, const PP_NaClFileInfo&)>
205 DownloadFileCallback;
207 void DownloadFile(PP_Instance instance,
208 const std::string& url,
209 const DownloadFileCallback& callback);
211 PP_Bool StartPpapiProxy(PP_Instance instance);
213 // Thin adapter from PPP_ManifestService to ManifestServiceChannel::Delegate.
214 // Note that user_data is managed by the caller of LaunchSelLdr. Please see
215 // also PP_ManifestService's comment for more details about resource
216 // management.
217 class ManifestServiceProxy : public ManifestServiceChannel::Delegate {
218 public:
219 ManifestServiceProxy(PP_Instance pp_instance, NaClAppProcessType process_type)
220 : pp_instance_(pp_instance), process_type_(process_type) {}
222 ~ManifestServiceProxy() override {}
224 void StartupInitializationComplete() override {
225 if (StartPpapiProxy(pp_instance_) == PP_TRUE) {
226 NaClPluginInstance* nacl_plugin_instance =
227 GetNaClPluginInstance(pp_instance_);
228 JsonManifest* manifest = GetJsonManifest(pp_instance_);
229 if (nacl_plugin_instance && manifest) {
230 NexeLoadManager* load_manager =
231 &nacl_plugin_instance->nexe_load_manager;
232 std::string full_url;
233 PP_PNaClOptions pnacl_options;
234 bool uses_nonsfi_mode;
235 JsonManifest::ErrorInfo error_info;
236 if (manifest->GetProgramURL(&full_url,
237 &pnacl_options,
238 &uses_nonsfi_mode,
239 &error_info)) {
240 int64_t exe_size = nacl_plugin_instance->pexe_size;
241 if (exe_size == 0)
242 exe_size = load_manager->nexe_size();
243 load_manager->ReportLoadSuccess(full_url, exe_size, exe_size);
249 void OpenResource(
250 const std::string& key,
251 const ManifestServiceChannel::OpenResourceCallback& callback) override {
252 DCHECK(ppapi::PpapiGlobals::Get()->GetMainThreadMessageLoop()->
253 BelongsToCurrentThread());
255 // For security hardening, disable open_resource() when it is isn't
256 // needed. PNaCl pexes can't use open_resource(), but general nexes
257 // and the PNaCl translator nexes may use it.
258 if (process_type_ != kNativeNaClProcessType &&
259 process_type_ != kPNaClTranslatorProcessType) {
260 // Return an error.
261 base::ThreadTaskRunnerHandle::Get()->PostTask(
262 FROM_HERE, base::Bind(callback, base::Passed(base::File()), 0, 0));
263 return;
266 std::string url;
267 // TODO(teravest): Clean up pnacl_options logic in JsonManifest so we don't
268 // have to initialize it like this here.
269 PP_PNaClOptions pnacl_options;
270 pnacl_options.translate = PP_FALSE;
271 pnacl_options.is_debug = PP_FALSE;
272 pnacl_options.use_subzero = PP_FALSE;
273 pnacl_options.opt_level = 2;
274 bool is_helper_process = process_type_ == kPNaClTranslatorProcessType;
275 if (!ManifestResolveKey(pp_instance_, is_helper_process, key, &url,
276 &pnacl_options)) {
277 base::ThreadTaskRunnerHandle::Get()->PostTask(
278 FROM_HERE, base::Bind(callback, base::Passed(base::File()), 0, 0));
279 return;
282 // We have to call DidDownloadFile, even if this object is destroyed, so
283 // that the handle inside PP_NaClFileInfo isn't leaked. This means that the
284 // callback passed to this function shouldn't have a weak pointer to an
285 // object either.
287 // TODO(teravest): Make a type like PP_NaClFileInfo to use for DownloadFile
288 // that would close the file handle on destruction.
289 DownloadFile(pp_instance_, url,
290 base::Bind(&ManifestServiceProxy::DidDownloadFile, callback));
293 private:
294 static void DidDownloadFile(
295 ManifestServiceChannel::OpenResourceCallback callback,
296 int32_t pp_error,
297 const PP_NaClFileInfo& file_info) {
298 if (pp_error != PP_OK) {
299 callback.Run(base::File(), 0, 0);
300 return;
302 callback.Run(base::File(file_info.handle),
303 file_info.token_lo,
304 file_info.token_hi);
307 PP_Instance pp_instance_;
308 NaClAppProcessType process_type_;
309 DISALLOW_COPY_AND_ASSIGN(ManifestServiceProxy);
312 blink::WebURLLoader* CreateWebURLLoader(const blink::WebDocument& document,
313 const GURL& gurl) {
314 blink::WebURLLoaderOptions options;
315 options.untrustedHTTP = true;
317 // Options settings here follow the original behavior in the trusted
318 // plugin and PepperURLLoaderHost.
319 if (document.securityOrigin().canRequest(gurl)) {
320 options.allowCredentials = true;
321 } else {
322 // Allow CORS.
323 options.crossOriginRequestPolicy =
324 blink::WebURLLoaderOptions::CrossOriginRequestPolicyUseAccessControl;
326 return document.frame()->createAssociatedURLLoader(options);
329 blink::WebURLRequest CreateWebURLRequest(const blink::WebDocument& document,
330 const GURL& gurl) {
331 blink::WebURLRequest request;
332 request.initialize();
333 request.setURL(gurl);
334 request.setFirstPartyForCookies(document.firstPartyForCookies());
335 return request;
338 int32_t FileDownloaderToPepperError(FileDownloader::Status status) {
339 switch (status) {
340 case FileDownloader::SUCCESS:
341 return PP_OK;
342 case FileDownloader::ACCESS_DENIED:
343 return PP_ERROR_NOACCESS;
344 case FileDownloader::FAILED:
345 return PP_ERROR_FAILED;
346 // No default case, to catch unhandled Status values.
348 return PP_ERROR_FAILED;
351 NaClAppProcessType PP_ToNaClAppProcessType(
352 PP_NaClAppProcessType pp_process_type) {
353 #define STATICALLY_CHECK_NACLAPPPROCESSTYPE_EQ(pp, nonpp) \
354 static_assert(static_cast<int>(pp) == static_cast<int>(nonpp), \
355 "PP_NaClAppProcessType differs from NaClAppProcessType");
356 STATICALLY_CHECK_NACLAPPPROCESSTYPE_EQ(PP_UNKNOWN_NACL_PROCESS_TYPE,
357 kUnknownNaClProcessType);
358 STATICALLY_CHECK_NACLAPPPROCESSTYPE_EQ(PP_NATIVE_NACL_PROCESS_TYPE,
359 kNativeNaClProcessType);
360 STATICALLY_CHECK_NACLAPPPROCESSTYPE_EQ(PP_PNACL_PROCESS_TYPE,
361 kPNaClProcessType);
362 STATICALLY_CHECK_NACLAPPPROCESSTYPE_EQ(PP_PNACL_TRANSLATOR_PROCESS_TYPE,
363 kPNaClTranslatorProcessType);
364 STATICALLY_CHECK_NACLAPPPROCESSTYPE_EQ(PP_NUM_NACL_PROCESS_TYPES,
365 kNumNaClProcessTypes);
366 #undef STATICALLY_CHECK_NACLAPPPROCESSTYPE_EQ
367 DCHECK(pp_process_type > PP_UNKNOWN_NACL_PROCESS_TYPE &&
368 pp_process_type < PP_NUM_NACL_PROCESS_TYPES);
369 return static_cast<NaClAppProcessType>(pp_process_type);
372 // Launch NaCl's sel_ldr process.
373 void LaunchSelLdr(PP_Instance instance,
374 PP_Bool main_service_runtime,
375 const char* alleged_url,
376 const PP_NaClFileInfo* nexe_file_info,
377 PP_Bool uses_nonsfi_mode,
378 PP_NaClAppProcessType pp_process_type,
379 void* imc_handle,
380 PP_CompletionCallback callback) {
381 CHECK(ppapi::PpapiGlobals::Get()->GetMainThreadMessageLoop()->
382 BelongsToCurrentThread());
383 NaClAppProcessType process_type = PP_ToNaClAppProcessType(pp_process_type);
384 // Create the manifest service proxy here, so on error case, it will be
385 // destructed (without passing it to ManifestServiceChannel).
386 scoped_ptr<ManifestServiceChannel::Delegate> manifest_service_proxy(
387 new ManifestServiceProxy(instance, process_type));
389 IPC::Sender* sender = content::RenderThread::Get();
390 DCHECK(sender);
391 int routing_id = GetRoutingID(instance);
392 NexeLoadManager* load_manager = GetNexeLoadManager(instance);
393 DCHECK(load_manager);
394 content::PepperPluginInstance* plugin_instance =
395 content::PepperPluginInstance::Get(instance);
396 DCHECK(plugin_instance);
397 if (!routing_id || !load_manager || !plugin_instance) {
398 if (nexe_file_info->handle != PP_kInvalidFileHandle) {
399 base::File closer(nexe_file_info->handle);
401 ppapi::PpapiGlobals::Get()->GetMainThreadMessageLoop()->PostTask(
402 FROM_HERE, base::Bind(callback.func, callback.user_data,
403 static_cast<int32_t>(PP_ERROR_FAILED)));
404 return;
407 InstanceInfo instance_info;
408 instance_info.url = GURL(alleged_url);
410 uint32_t perm_bits = ppapi::PERMISSION_NONE;
411 // Conditionally block 'Dev' interfaces. We do this for the NaCl process, so
412 // it's clearer to developers when they are using 'Dev' inappropriately. We
413 // must also check on the trusted side of the proxy.
414 if (load_manager->DevInterfacesEnabled())
415 perm_bits |= ppapi::PERMISSION_DEV;
416 instance_info.permissions =
417 ppapi::PpapiPermissions::GetForCommandLine(perm_bits);
419 std::vector<NaClResourcePrefetchRequest> resource_prefetch_request_list;
420 if (process_type == kNativeNaClProcessType) {
421 JsonManifest* manifest = GetJsonManifest(instance);
422 if (manifest) {
423 manifest->GetPrefetchableFiles(&resource_prefetch_request_list);
425 for (size_t i = 0; i < resource_prefetch_request_list.size(); ++i) {
426 const GURL gurl(resource_prefetch_request_list[i].resource_url);
427 // Important security check. Do not remove.
428 if (!CanOpenViaFastPath(plugin_instance, gurl)) {
429 resource_prefetch_request_list.clear();
430 break;
436 IPC::PlatformFileForTransit nexe_for_transit =
437 IPC::InvalidPlatformFileForTransit();
438 #if defined(OS_POSIX)
439 if (nexe_file_info->handle != PP_kInvalidFileHandle)
440 nexe_for_transit = base::FileDescriptor(nexe_file_info->handle, true);
441 #elif defined(OS_WIN)
442 // Duplicate the handle on the browser side instead of the renderer.
443 // This is because BrokerGetFileForProcess isn't part of content/public, and
444 // it's simpler to do the duplication in the browser anyway.
445 nexe_for_transit = nexe_file_info->handle;
446 #else
447 # error Unsupported target platform.
448 #endif
450 std::string error_message_string;
451 NaClLaunchResult launch_result;
452 if (!sender->Send(new NaClHostMsg_LaunchNaCl(
453 NaClLaunchParams(
454 instance_info.url.spec(),
455 nexe_for_transit,
456 nexe_file_info->token_lo,
457 nexe_file_info->token_hi,
458 resource_prefetch_request_list,
459 routing_id,
460 perm_bits,
461 PP_ToBool(uses_nonsfi_mode),
462 process_type),
463 &launch_result,
464 &error_message_string))) {
465 ppapi::PpapiGlobals::Get()->GetMainThreadMessageLoop()->PostTask(
466 FROM_HERE,
467 base::Bind(callback.func, callback.user_data,
468 static_cast<int32_t>(PP_ERROR_FAILED)));
469 return;
472 load_manager->set_nonsfi(PP_ToBool(uses_nonsfi_mode));
474 if (!error_message_string.empty()) {
475 // Even on error, some FDs/handles may be passed to here.
476 // We must release those resources.
477 // See also nacl_process_host.cc.
478 IPC::PlatformFileForTransitToFile(launch_result.imc_channel_handle);
479 base::SharedMemory::CloseHandle(launch_result.crash_info_shmem_handle);
481 if (PP_ToBool(main_service_runtime)) {
482 load_manager->ReportLoadError(PP_NACL_ERROR_SEL_LDR_LAUNCH,
483 "ServiceRuntime: failed to start",
484 error_message_string);
486 ppapi::PpapiGlobals::Get()->GetMainThreadMessageLoop()->PostTask(
487 FROM_HERE,
488 base::Bind(callback.func, callback.user_data,
489 static_cast<int32_t>(PP_ERROR_FAILED)));
490 return;
493 instance_info.channel_handle = launch_result.ppapi_ipc_channel_handle;
494 instance_info.plugin_pid = launch_result.plugin_pid;
495 instance_info.plugin_child_id = launch_result.plugin_child_id;
497 // Don't save instance_info if channel handle is invalid.
498 if (IsValidChannelHandle(instance_info.channel_handle)) {
499 NaClPluginInstance* nacl_plugin_instance = GetNaClPluginInstance(instance);
500 nacl_plugin_instance->instance_info.reset(new InstanceInfo(instance_info));
503 *(static_cast<NaClHandle*>(imc_handle)) =
504 IPC::PlatformFileForTransitToPlatformFile(
505 launch_result.imc_channel_handle);
507 // Store the crash information shared memory handle.
508 load_manager->set_crash_info_shmem_handle(
509 launch_result.crash_info_shmem_handle);
511 // Create the trusted plugin channel.
512 if (IsValidChannelHandle(launch_result.trusted_ipc_channel_handle)) {
513 bool is_helper_nexe = !PP_ToBool(main_service_runtime);
514 scoped_ptr<TrustedPluginChannel> trusted_plugin_channel(
515 new TrustedPluginChannel(
516 load_manager,
517 launch_result.trusted_ipc_channel_handle,
518 content::RenderThread::Get()->GetShutdownEvent(),
519 is_helper_nexe));
520 load_manager->set_trusted_plugin_channel(trusted_plugin_channel.Pass());
521 } else {
522 PostPPCompletionCallback(callback, PP_ERROR_FAILED);
523 return;
526 // Create the manifest service handle as well.
527 if (IsValidChannelHandle(launch_result.manifest_service_ipc_channel_handle)) {
528 scoped_ptr<ManifestServiceChannel> manifest_service_channel(
529 new ManifestServiceChannel(
530 launch_result.manifest_service_ipc_channel_handle,
531 base::Bind(&PostPPCompletionCallback, callback),
532 manifest_service_proxy.Pass(),
533 content::RenderThread::Get()->GetShutdownEvent()));
534 load_manager->set_manifest_service_channel(
535 manifest_service_channel.Pass());
539 PP_Bool StartPpapiProxy(PP_Instance instance) {
540 NexeLoadManager* load_manager = GetNexeLoadManager(instance);
541 DCHECK(load_manager);
542 if (!load_manager)
543 return PP_FALSE;
545 content::PepperPluginInstance* plugin_instance =
546 content::PepperPluginInstance::Get(instance);
547 if (!plugin_instance) {
548 DLOG(ERROR) << "GetInstance() failed";
549 return PP_FALSE;
552 NaClPluginInstance* nacl_plugin_instance = GetNaClPluginInstance(instance);
553 if (!nacl_plugin_instance->instance_info) {
554 DLOG(ERROR) << "Could not find instance ID";
555 return PP_FALSE;
557 scoped_ptr<InstanceInfo> instance_info =
558 nacl_plugin_instance->instance_info.Pass();
560 PP_ExternalPluginResult result = plugin_instance->SwitchToOutOfProcessProxy(
561 base::FilePath().AppendASCII(instance_info->url.spec()),
562 instance_info->permissions,
563 instance_info->channel_handle,
564 instance_info->plugin_pid,
565 instance_info->plugin_child_id);
567 if (result == PP_EXTERNAL_PLUGIN_OK) {
568 // Log the amound of time that has passed between the trusted plugin being
569 // initialized and the untrusted plugin being initialized. This is
570 // (roughly) the cost of using NaCl, in terms of startup time.
571 load_manager->ReportStartupOverhead();
572 return PP_TRUE;
573 } else if (result == PP_EXTERNAL_PLUGIN_ERROR_MODULE) {
574 load_manager->ReportLoadError(PP_NACL_ERROR_START_PROXY_MODULE,
575 "could not initialize module.");
576 } else if (result == PP_EXTERNAL_PLUGIN_ERROR_INSTANCE) {
577 load_manager->ReportLoadError(PP_NACL_ERROR_START_PROXY_MODULE,
578 "could not create instance.");
580 return PP_FALSE;
583 int UrandomFD(void) {
584 #if defined(OS_POSIX)
585 return base::GetUrandomFD();
586 #else
587 return -1;
588 #endif
591 int32_t BrokerDuplicateHandle(PP_FileHandle source_handle,
592 uint32_t process_id,
593 PP_FileHandle* target_handle,
594 uint32_t desired_access,
595 uint32_t options) {
596 #if defined(OS_WIN)
597 return content::BrokerDuplicateHandle(source_handle, process_id,
598 target_handle, desired_access,
599 options);
600 #else
601 return 0;
602 #endif
605 // Convert a URL to a filename for GetReadonlyPnaclFd.
606 // Must be kept in sync with PnaclCanOpenFile() in
607 // components/nacl/browser/nacl_file_host.cc.
608 std::string PnaclComponentURLToFilename(const std::string& url) {
609 // PNaCl component URLs aren't arbitrary URLs; they are always either
610 // generated from ManifestResolveKey or PnaclResources::ReadResourceInfo.
611 // So, it's safe to just use string parsing operations here instead of
612 // URL-parsing ones.
613 DCHECK(base::StartsWithASCII(url, kPNaClTranslatorBaseUrl, true));
614 std::string r = url.substr(std::string(kPNaClTranslatorBaseUrl).length());
616 // Use white-listed-chars.
617 size_t replace_pos;
618 static const char* white_list = "abcdefghijklmnopqrstuvwxyz0123456789_";
619 replace_pos = r.find_first_not_of(white_list);
620 while(replace_pos != std::string::npos) {
621 r = r.replace(replace_pos, 1, "_");
622 replace_pos = r.find_first_not_of(white_list);
624 return r;
627 PP_FileHandle GetReadonlyPnaclFd(const char* url,
628 bool is_executable,
629 uint64_t* nonce_lo,
630 uint64_t* nonce_hi) {
631 std::string filename = PnaclComponentURLToFilename(url);
632 IPC::PlatformFileForTransit out_fd = IPC::InvalidPlatformFileForTransit();
633 IPC::Sender* sender = content::RenderThread::Get();
634 DCHECK(sender);
635 if (!sender->Send(new NaClHostMsg_GetReadonlyPnaclFD(
636 std::string(filename), is_executable,
637 &out_fd, nonce_lo, nonce_hi))) {
638 return PP_kInvalidFileHandle;
640 if (out_fd == IPC::InvalidPlatformFileForTransit()) {
641 return PP_kInvalidFileHandle;
643 return IPC::PlatformFileForTransitToPlatformFile(out_fd);
646 void GetReadExecPnaclFd(const char* url,
647 PP_NaClFileInfo* out_file_info) {
648 *out_file_info = kInvalidNaClFileInfo;
649 out_file_info->handle = GetReadonlyPnaclFd(url, true /* is_executable */,
650 &out_file_info->token_lo,
651 &out_file_info->token_hi);
654 PP_FileHandle CreateTemporaryFile(PP_Instance instance) {
655 IPC::PlatformFileForTransit transit_fd = IPC::InvalidPlatformFileForTransit();
656 IPC::Sender* sender = content::RenderThread::Get();
657 DCHECK(sender);
658 if (!sender->Send(new NaClHostMsg_NaClCreateTemporaryFile(
659 &transit_fd))) {
660 return PP_kInvalidFileHandle;
663 if (transit_fd == IPC::InvalidPlatformFileForTransit()) {
664 return PP_kInvalidFileHandle;
667 return IPC::PlatformFileForTransitToPlatformFile(transit_fd);
670 int32_t GetNumberOfProcessors() {
671 IPC::Sender* sender = content::RenderThread::Get();
672 DCHECK(sender);
673 int32_t num_processors = 1;
674 return sender->Send(new NaClHostMsg_NaClGetNumProcessors(&num_processors)) ?
675 num_processors : 1;
678 void GetNexeFd(PP_Instance instance,
679 const std::string& pexe_url,
680 uint32_t opt_level,
681 const base::Time& last_modified_time,
682 const std::string& etag,
683 bool has_no_store_header,
684 bool use_subzero,
685 base::Callback<void(int32_t, bool, PP_FileHandle)> callback) {
686 if (!InitializePnaclResourceHost()) {
687 ppapi::PpapiGlobals::Get()->GetMainThreadMessageLoop()->PostTask(
688 FROM_HERE,
689 base::Bind(callback,
690 static_cast<int32_t>(PP_ERROR_FAILED),
691 false,
692 PP_kInvalidFileHandle));
693 return;
696 PnaclCacheInfo cache_info;
697 cache_info.pexe_url = GURL(pexe_url);
698 // TODO(dschuff): Get this value from the pnacl json file after it
699 // rolls in from NaCl.
700 cache_info.abi_version = 1;
701 cache_info.opt_level = opt_level;
702 cache_info.last_modified = last_modified_time;
703 cache_info.etag = etag;
704 cache_info.has_no_store_header = has_no_store_header;
705 cache_info.use_subzero = use_subzero;
706 cache_info.sandbox_isa = GetSandboxArch();
707 cache_info.extra_flags = GetCpuFeatures();
709 g_pnacl_resource_host.Get()->RequestNexeFd(
710 GetRoutingID(instance),
711 instance,
712 cache_info,
713 callback);
716 void LogTranslationFinishedUMA(const std::string& uma_suffix,
717 int32_t opt_level,
718 int32_t unknown_opt_level,
719 int64_t nexe_size,
720 int64_t pexe_size,
721 int64_t compile_time_us,
722 base::TimeDelta total_time) {
723 HistogramEnumerate("NaCl.Options.PNaCl.OptLevel" + uma_suffix, opt_level,
724 unknown_opt_level + 1);
725 HistogramKBPerSec("NaCl.Perf.PNaClLoadTime.CompileKBPerSec" + uma_suffix,
726 pexe_size / 1024, compile_time_us);
727 HistogramSizeKB("NaCl.Perf.Size.PNaClTranslatedNexe" + uma_suffix,
728 nexe_size / 1024);
729 HistogramSizeKB("NaCl.Perf.Size.Pexe" + uma_suffix, pexe_size / 1024);
730 HistogramRatio("NaCl.Perf.Size.PexeNexeSizePct" + uma_suffix, pexe_size,
731 nexe_size);
732 HistogramTimeTranslation(
733 "NaCl.Perf.PNaClLoadTime.TotalUncachedTime" + uma_suffix,
734 total_time.InMilliseconds());
735 HistogramKBPerSec(
736 "NaCl.Perf.PNaClLoadTime.TotalUncachedKBPerSec" + uma_suffix,
737 pexe_size / 1024, total_time.InMicroseconds());
740 void ReportTranslationFinished(PP_Instance instance,
741 PP_Bool success,
742 int32_t opt_level,
743 PP_Bool use_subzero,
744 int64_t nexe_size,
745 int64_t pexe_size,
746 int64_t compile_time_us) {
747 NexeLoadManager* load_manager = GetNexeLoadManager(instance);
748 DCHECK(load_manager);
749 if (success == PP_TRUE && load_manager) {
750 base::TimeDelta total_time =
751 base::Time::Now() - load_manager->pnacl_start_time();
752 static const int32_t kUnknownOptLevel = 4;
753 if (opt_level < 0 || opt_level > 3)
754 opt_level = kUnknownOptLevel;
755 // Log twice: once to cover all PNaCl UMA, and then a second
756 // time with the more specific UMA (Subzero vs LLC).
757 std::string uma_suffix(use_subzero ? ".Subzero" : ".LLC");
758 LogTranslationFinishedUMA("", opt_level, kUnknownOptLevel, nexe_size,
759 pexe_size, compile_time_us, total_time);
760 LogTranslationFinishedUMA(uma_suffix, opt_level, kUnknownOptLevel,
761 nexe_size, pexe_size, compile_time_us,
762 total_time);
765 // If the resource host isn't initialized, don't try to do that here.
766 // Just return because something is already very wrong.
767 if (g_pnacl_resource_host.Get().get() == NULL)
768 return;
769 g_pnacl_resource_host.Get()->ReportTranslationFinished(instance, success);
771 // Record the pexe size for reporting in a later load event.
772 NaClPluginInstance* nacl_plugin_instance = GetNaClPluginInstance(instance);
773 if (nacl_plugin_instance) {
774 nacl_plugin_instance->pexe_size = pexe_size;
778 PP_FileHandle OpenNaClExecutable(PP_Instance instance,
779 const char* file_url,
780 uint64_t* nonce_lo,
781 uint64_t* nonce_hi) {
782 NexeLoadManager* load_manager = GetNexeLoadManager(instance);
783 DCHECK(load_manager);
784 if (!load_manager)
785 return PP_kInvalidFileHandle;
787 content::PepperPluginInstance* plugin_instance =
788 content::PepperPluginInstance::Get(instance);
789 if (!plugin_instance)
790 return PP_kInvalidFileHandle;
792 GURL gurl(file_url);
793 // Important security check. Do not remove.
794 if (!CanOpenViaFastPath(plugin_instance, gurl))
795 return PP_kInvalidFileHandle;
797 IPC::PlatformFileForTransit out_fd = IPC::InvalidPlatformFileForTransit();
798 IPC::Sender* sender = content::RenderThread::Get();
799 DCHECK(sender);
800 *nonce_lo = 0;
801 *nonce_hi = 0;
802 base::FilePath file_path;
803 if (!sender->Send(
804 new NaClHostMsg_OpenNaClExecutable(GetRoutingID(instance),
805 GURL(file_url),
806 !load_manager->nonsfi(),
807 &out_fd,
808 nonce_lo,
809 nonce_hi))) {
810 return PP_kInvalidFileHandle;
813 if (out_fd == IPC::InvalidPlatformFileForTransit())
814 return PP_kInvalidFileHandle;
816 return IPC::PlatformFileForTransitToPlatformFile(out_fd);
819 void DispatchEvent(PP_Instance instance,
820 PP_NaClEventType event_type,
821 const char* resource_url,
822 PP_Bool length_is_computable,
823 uint64_t loaded_bytes,
824 uint64_t total_bytes) {
825 ProgressEvent event(event_type,
826 resource_url,
827 PP_ToBool(length_is_computable),
828 loaded_bytes,
829 total_bytes);
830 DispatchProgressEvent(instance, event);
833 void ReportLoadError(PP_Instance instance,
834 PP_NaClError error,
835 const char* error_message) {
836 NexeLoadManager* load_manager = GetNexeLoadManager(instance);
837 if (load_manager)
838 load_manager->ReportLoadError(error, error_message);
841 void InstanceCreated(PP_Instance instance) {
842 InstanceMap& map = g_instance_map.Get();
843 CHECK(map.find(instance) == map.end()); // Sanity check.
844 scoped_ptr<NaClPluginInstance> new_instance(new NaClPluginInstance(instance));
845 map.add(instance, new_instance.Pass());
848 void InstanceDestroyed(PP_Instance instance) {
849 InstanceMap& map = g_instance_map.Get();
850 InstanceMap::iterator iter = map.find(instance);
851 CHECK(iter != map.end());
852 // The erase may call NexeLoadManager's destructor prior to removing it from
853 // the map. In that case, it is possible for the trusted Plugin to re-enter
854 // the NexeLoadManager (e.g., by calling ReportLoadError). Passing out the
855 // NexeLoadManager to a local scoped_ptr just ensures that its entry is gone
856 // from the map prior to the destructor being invoked.
857 scoped_ptr<NaClPluginInstance> temp(map.take(instance));
858 map.erase(iter);
861 PP_Bool NaClDebugEnabledForURL(const char* alleged_nmf_url) {
862 if (!base::CommandLine::ForCurrentProcess()->HasSwitch(
863 switches::kEnableNaClDebug))
864 return PP_FALSE;
865 IPC::Sender* sender = content::RenderThread::Get();
866 DCHECK(sender);
867 bool should_debug = false;
868 return PP_FromBool(
869 sender->Send(new NaClHostMsg_NaClDebugEnabledForURL(GURL(alleged_nmf_url),
870 &should_debug)) &&
871 should_debug);
874 void Vlog(const char* message) {
875 VLOG(1) << message;
878 void InitializePlugin(PP_Instance instance,
879 uint32_t argc,
880 const char* argn[],
881 const char* argv[]) {
882 NexeLoadManager* load_manager = GetNexeLoadManager(instance);
883 DCHECK(load_manager);
884 if (load_manager)
885 load_manager->InitializePlugin(argc, argn, argv);
888 void DownloadManifestToBuffer(PP_Instance instance,
889 struct PP_CompletionCallback callback);
891 bool CreateJsonManifest(PP_Instance instance,
892 const std::string& manifest_url,
893 const std::string& manifest_data);
895 void RequestNaClManifest(PP_Instance instance,
896 PP_CompletionCallback callback) {
897 NexeLoadManager* load_manager = GetNexeLoadManager(instance);
898 DCHECK(load_manager);
899 if (!load_manager) {
900 ppapi::PpapiGlobals::Get()->GetMainThreadMessageLoop()->PostTask(
901 FROM_HERE,
902 base::Bind(callback.func, callback.user_data,
903 static_cast<int32_t>(PP_ERROR_FAILED)));
904 return;
907 std::string url = load_manager->GetManifestURLArgument();
908 if (url.empty() || !load_manager->RequestNaClManifest(url)) {
909 ppapi::PpapiGlobals::Get()->GetMainThreadMessageLoop()->PostTask(
910 FROM_HERE,
911 base::Bind(callback.func, callback.user_data,
912 static_cast<int32_t>(PP_ERROR_FAILED)));
913 return;
916 const GURL& base_url = load_manager->manifest_base_url();
917 if (base_url.SchemeIs("data")) {
918 GURL gurl(base_url);
919 std::string mime_type;
920 std::string charset;
921 std::string data;
922 int32_t error = PP_ERROR_FAILED;
923 if (net::DataURL::Parse(gurl, &mime_type, &charset, &data)) {
924 if (data.size() <= ManifestDownloader::kNaClManifestMaxFileBytes) {
925 if (CreateJsonManifest(instance, base_url.spec(), data))
926 error = PP_OK;
927 } else {
928 load_manager->ReportLoadError(PP_NACL_ERROR_MANIFEST_TOO_LARGE,
929 "manifest file too large.");
931 } else {
932 load_manager->ReportLoadError(PP_NACL_ERROR_MANIFEST_LOAD_URL,
933 "could not load manifest url.");
935 ppapi::PpapiGlobals::Get()->GetMainThreadMessageLoop()->PostTask(
936 FROM_HERE,
937 base::Bind(callback.func, callback.user_data, error));
938 } else {
939 DownloadManifestToBuffer(instance, callback);
943 PP_Var GetManifestBaseURL(PP_Instance instance) {
944 NexeLoadManager* load_manager = GetNexeLoadManager(instance);
945 DCHECK(load_manager);
946 if (!load_manager)
947 return PP_MakeUndefined();
948 const GURL& gurl = load_manager->manifest_base_url();
949 if (!gurl.is_valid())
950 return PP_MakeUndefined();
951 return ppapi::StringVar::StringToPPVar(gurl.spec());
954 void ProcessNaClManifest(PP_Instance instance, const char* program_url) {
955 nacl::NexeLoadManager* load_manager = GetNexeLoadManager(instance);
956 if (load_manager)
957 load_manager->ProcessNaClManifest(program_url);
960 void DownloadManifestToBufferCompletion(PP_Instance instance,
961 struct PP_CompletionCallback callback,
962 base::Time start_time,
963 PP_NaClError pp_nacl_error,
964 const std::string& data);
966 void DownloadManifestToBuffer(PP_Instance instance,
967 struct PP_CompletionCallback callback) {
968 nacl::NexeLoadManager* load_manager = GetNexeLoadManager(instance);
969 DCHECK(load_manager);
970 content::PepperPluginInstance* plugin_instance =
971 content::PepperPluginInstance::Get(instance);
972 if (!load_manager || !plugin_instance) {
973 ppapi::PpapiGlobals::Get()->GetMainThreadMessageLoop()->PostTask(
974 FROM_HERE,
975 base::Bind(callback.func, callback.user_data,
976 static_cast<int32_t>(PP_ERROR_FAILED)));
978 const blink::WebDocument& document =
979 plugin_instance->GetContainer()->element().document();
981 const GURL& gurl = load_manager->manifest_base_url();
982 scoped_ptr<blink::WebURLLoader> url_loader(
983 CreateWebURLLoader(document, gurl));
984 blink::WebURLRequest request = CreateWebURLRequest(document, gurl);
986 // ManifestDownloader deletes itself after invoking the callback.
987 ManifestDownloader* manifest_downloader = new ManifestDownloader(
988 url_loader.Pass(),
989 load_manager->is_installed(),
990 base::Bind(DownloadManifestToBufferCompletion,
991 instance, callback, base::Time::Now()));
992 manifest_downloader->Load(request);
995 void DownloadManifestToBufferCompletion(PP_Instance instance,
996 struct PP_CompletionCallback callback,
997 base::Time start_time,
998 PP_NaClError pp_nacl_error,
999 const std::string& data) {
1000 base::TimeDelta download_time = base::Time::Now() - start_time;
1001 HistogramTimeSmall("NaCl.Perf.StartupTime.ManifestDownload",
1002 download_time.InMilliseconds());
1004 nacl::NexeLoadManager* load_manager = GetNexeLoadManager(instance);
1005 if (!load_manager) {
1006 callback.func(callback.user_data, PP_ERROR_ABORTED);
1007 return;
1010 int32_t pp_error;
1011 switch (pp_nacl_error) {
1012 case PP_NACL_ERROR_LOAD_SUCCESS:
1013 pp_error = PP_OK;
1014 break;
1015 case PP_NACL_ERROR_MANIFEST_LOAD_URL:
1016 pp_error = PP_ERROR_FAILED;
1017 load_manager->ReportLoadError(PP_NACL_ERROR_MANIFEST_LOAD_URL,
1018 "could not load manifest url.");
1019 break;
1020 case PP_NACL_ERROR_MANIFEST_TOO_LARGE:
1021 pp_error = PP_ERROR_FILETOOBIG;
1022 load_manager->ReportLoadError(PP_NACL_ERROR_MANIFEST_TOO_LARGE,
1023 "manifest file too large.");
1024 break;
1025 case PP_NACL_ERROR_MANIFEST_NOACCESS_URL:
1026 pp_error = PP_ERROR_NOACCESS;
1027 load_manager->ReportLoadError(PP_NACL_ERROR_MANIFEST_NOACCESS_URL,
1028 "access to manifest url was denied.");
1029 break;
1030 default:
1031 NOTREACHED();
1032 pp_error = PP_ERROR_FAILED;
1033 load_manager->ReportLoadError(PP_NACL_ERROR_MANIFEST_LOAD_URL,
1034 "could not load manifest url.");
1037 if (pp_error == PP_OK) {
1038 std::string base_url = load_manager->manifest_base_url().spec();
1039 if (!CreateJsonManifest(instance, base_url, data))
1040 pp_error = PP_ERROR_FAILED;
1042 callback.func(callback.user_data, pp_error);
1045 bool CreateJsonManifest(PP_Instance instance,
1046 const std::string& manifest_url,
1047 const std::string& manifest_data) {
1048 HistogramSizeKB("NaCl.Perf.Size.Manifest",
1049 static_cast<int32_t>(manifest_data.length() / 1024));
1051 nacl::NexeLoadManager* load_manager = GetNexeLoadManager(instance);
1052 if (!load_manager)
1053 return false;
1055 const char* isa_type;
1056 if (load_manager->IsPNaCl())
1057 isa_type = kPortableArch;
1058 else
1059 isa_type = GetSandboxArch();
1061 scoped_ptr<nacl::JsonManifest> j(
1062 new nacl::JsonManifest(
1063 manifest_url.c_str(),
1064 isa_type,
1065 IsNonSFIModeEnabled(),
1066 PP_ToBool(NaClDebugEnabledForURL(manifest_url.c_str()))));
1067 JsonManifest::ErrorInfo error_info;
1068 if (j->Init(manifest_data.c_str(), &error_info)) {
1069 GetNaClPluginInstance(instance)->json_manifest.reset(j.release());
1070 return true;
1072 load_manager->ReportLoadError(error_info.error, error_info.string);
1073 return false;
1076 PP_Bool ManifestGetProgramURL(PP_Instance instance,
1077 PP_Var* pp_full_url,
1078 PP_PNaClOptions* pnacl_options,
1079 PP_Bool* pp_uses_nonsfi_mode) {
1080 nacl::NexeLoadManager* load_manager = GetNexeLoadManager(instance);
1082 JsonManifest* manifest = GetJsonManifest(instance);
1083 if (manifest == NULL)
1084 return PP_FALSE;
1086 bool uses_nonsfi_mode;
1087 std::string full_url;
1088 JsonManifest::ErrorInfo error_info;
1089 if (manifest->GetProgramURL(&full_url, pnacl_options, &uses_nonsfi_mode,
1090 &error_info)) {
1091 *pp_full_url = ppapi::StringVar::StringToPPVar(full_url);
1092 *pp_uses_nonsfi_mode = PP_FromBool(uses_nonsfi_mode);
1093 // Check if we should use Subzero (x86-32 / non-debugging case for now).
1094 if (pnacl_options->opt_level == 0 && !pnacl_options->is_debug &&
1095 strcmp(GetSandboxArch(), "x86-32") == 0 &&
1096 base::CommandLine::ForCurrentProcess()->HasSwitch(
1097 switches::kEnablePNaClSubzero)) {
1098 pnacl_options->use_subzero = PP_TRUE;
1099 // Subzero -O2 is closer to LLC -O0, so indicate -O2.
1100 pnacl_options->opt_level = 2;
1102 return PP_TRUE;
1105 if (load_manager)
1106 load_manager->ReportLoadError(error_info.error, error_info.string);
1107 return PP_FALSE;
1110 bool ManifestResolveKey(PP_Instance instance,
1111 bool is_helper_process,
1112 const std::string& key,
1113 std::string* full_url,
1114 PP_PNaClOptions* pnacl_options) {
1115 // For "helper" processes (llc and ld, for PNaCl translation), we resolve
1116 // keys manually as there is no existing .nmf file to parse.
1117 if (is_helper_process) {
1118 pnacl_options->translate = PP_FALSE;
1119 *full_url = std::string(kPNaClTranslatorBaseUrl) + GetSandboxArch() + "/" +
1120 key;
1121 return true;
1124 JsonManifest* manifest = GetJsonManifest(instance);
1125 if (manifest == NULL)
1126 return false;
1128 return manifest->ResolveKey(key, full_url, pnacl_options);
1131 PP_Bool GetPNaClResourceInfo(PP_Instance instance,
1132 PP_Var* llc_tool_name,
1133 PP_Var* ld_tool_name,
1134 PP_Var* subzero_tool_name) {
1135 static const char kFilename[] = "chrome://pnacl-translator/pnacl.json";
1136 NexeLoadManager* load_manager = GetNexeLoadManager(instance);
1137 DCHECK(load_manager);
1138 if (!load_manager)
1139 return PP_FALSE;
1141 uint64_t nonce_lo = 0;
1142 uint64_t nonce_hi = 0;
1143 base::File file(GetReadonlyPnaclFd(kFilename, false /* is_executable */,
1144 &nonce_lo, &nonce_hi));
1145 if (!file.IsValid()) {
1146 load_manager->ReportLoadError(
1147 PP_NACL_ERROR_PNACL_RESOURCE_FETCH,
1148 "The Portable Native Client (pnacl) component is not "
1149 "installed. Please consult chrome://components for more "
1150 "information.");
1151 return PP_FALSE;
1154 base::File::Info file_info;
1155 if (!file.GetInfo(&file_info)) {
1156 load_manager->ReportLoadError(
1157 PP_NACL_ERROR_PNACL_RESOURCE_FETCH,
1158 std::string("GetPNaClResourceInfo, GetFileInfo failed for: ") +
1159 kFilename);
1160 return PP_FALSE;
1163 if (file_info.size > 1 << 20) {
1164 load_manager->ReportLoadError(
1165 PP_NACL_ERROR_PNACL_RESOURCE_FETCH,
1166 std::string("GetPNaClResourceInfo, file too large: ") + kFilename);
1167 return PP_FALSE;
1170 scoped_ptr<char[]> buffer(new char[file_info.size + 1]);
1171 if (buffer.get() == NULL) {
1172 load_manager->ReportLoadError(
1173 PP_NACL_ERROR_PNACL_RESOURCE_FETCH,
1174 std::string("GetPNaClResourceInfo, couldn't allocate for: ") +
1175 kFilename);
1176 return PP_FALSE;
1179 int rc = file.Read(0, buffer.get(), file_info.size);
1180 if (rc < 0) {
1181 load_manager->ReportLoadError(
1182 PP_NACL_ERROR_PNACL_RESOURCE_FETCH,
1183 std::string("GetPNaClResourceInfo, reading failed for: ") + kFilename);
1184 return PP_FALSE;
1187 // Null-terminate the bytes we we read from the file.
1188 buffer.get()[rc] = 0;
1190 // Expect the JSON file to contain a top-level object (dictionary).
1191 base::JSONReader json_reader;
1192 int json_read_error_code;
1193 std::string json_read_error_msg;
1194 scoped_ptr<base::Value> json_data(json_reader.ReadAndReturnError(
1195 buffer.get(),
1196 base::JSON_PARSE_RFC,
1197 &json_read_error_code,
1198 &json_read_error_msg));
1199 if (!json_data) {
1200 load_manager->ReportLoadError(
1201 PP_NACL_ERROR_PNACL_RESOURCE_FETCH,
1202 std::string("Parsing resource info failed: JSON parse error: ") +
1203 json_read_error_msg);
1204 return PP_FALSE;
1207 base::DictionaryValue* json_dict;
1208 if (!json_data->GetAsDictionary(&json_dict)) {
1209 load_manager->ReportLoadError(
1210 PP_NACL_ERROR_PNACL_RESOURCE_FETCH,
1211 "Parsing resource info failed: Malformed JSON dictionary");
1212 return PP_FALSE;
1215 std::string pnacl_llc_name;
1216 if (json_dict->GetString("pnacl-llc-name", &pnacl_llc_name))
1217 *llc_tool_name = ppapi::StringVar::StringToPPVar(pnacl_llc_name);
1219 std::string pnacl_ld_name;
1220 if (json_dict->GetString("pnacl-ld-name", &pnacl_ld_name))
1221 *ld_tool_name = ppapi::StringVar::StringToPPVar(pnacl_ld_name);
1223 std::string pnacl_sz_name;
1224 if (json_dict->GetString("pnacl-sz-name", &pnacl_sz_name)) {
1225 *subzero_tool_name = ppapi::StringVar::StringToPPVar(pnacl_sz_name);
1226 } else {
1227 // TODO(jvoung): remove fallback after one chrome release
1228 // or when we bump the kMinPnaclVersion.
1229 // TODO(jvoung): Just use strings instead of PP_Var!
1230 *subzero_tool_name = ppapi::StringVar::StringToPPVar("pnacl-sz.nexe");
1233 return PP_TRUE;
1236 PP_Var GetCpuFeatureAttrs() {
1237 return ppapi::StringVar::StringToPPVar(GetCpuFeatures());
1240 // Encapsulates some of the state for a call to DownloadNexe to prevent
1241 // argument lists from getting too long.
1242 struct DownloadNexeRequest {
1243 PP_Instance instance;
1244 std::string url;
1245 PP_CompletionCallback callback;
1246 base::Time start_time;
1249 // A utility class to ensure that we don't send progress events more often than
1250 // every 10ms for a given file.
1251 class ProgressEventRateLimiter {
1252 public:
1253 explicit ProgressEventRateLimiter(PP_Instance instance)
1254 : instance_(instance) { }
1256 void ReportProgress(const std::string& url,
1257 int64_t total_bytes_received,
1258 int64_t total_bytes_to_be_received) {
1259 base::Time now = base::Time::Now();
1260 if (now - last_event_ > base::TimeDelta::FromMilliseconds(10)) {
1261 DispatchProgressEvent(instance_,
1262 ProgressEvent(PP_NACL_EVENT_PROGRESS,
1263 url,
1264 total_bytes_to_be_received >= 0,
1265 total_bytes_received,
1266 total_bytes_to_be_received));
1267 last_event_ = now;
1271 private:
1272 PP_Instance instance_;
1273 base::Time last_event_;
1276 void DownloadNexeCompletion(const DownloadNexeRequest& request,
1277 PP_NaClFileInfo* out_file_info,
1278 FileDownloader::Status status,
1279 base::File target_file,
1280 int http_status);
1282 void DownloadNexe(PP_Instance instance,
1283 const char* url,
1284 PP_NaClFileInfo* out_file_info,
1285 PP_CompletionCallback callback) {
1286 CHECK(url);
1287 CHECK(out_file_info);
1288 DownloadNexeRequest request;
1289 request.instance = instance;
1290 request.url = url;
1291 request.callback = callback;
1292 request.start_time = base::Time::Now();
1294 // Try the fast path for retrieving the file first.
1295 PP_FileHandle handle = OpenNaClExecutable(instance,
1296 url,
1297 &out_file_info->token_lo,
1298 &out_file_info->token_hi);
1299 if (handle != PP_kInvalidFileHandle) {
1300 DownloadNexeCompletion(request,
1301 out_file_info,
1302 FileDownloader::SUCCESS,
1303 base::File(handle),
1304 200);
1305 return;
1308 // The fast path didn't work, we'll fetch the file using URLLoader and write
1309 // it to local storage.
1310 base::File target_file(CreateTemporaryFile(instance));
1311 GURL gurl(url);
1313 content::PepperPluginInstance* plugin_instance =
1314 content::PepperPluginInstance::Get(instance);
1315 if (!plugin_instance) {
1316 ppapi::PpapiGlobals::Get()->GetMainThreadMessageLoop()->PostTask(
1317 FROM_HERE,
1318 base::Bind(callback.func, callback.user_data,
1319 static_cast<int32_t>(PP_ERROR_FAILED)));
1321 const blink::WebDocument& document =
1322 plugin_instance->GetContainer()->element().document();
1323 scoped_ptr<blink::WebURLLoader> url_loader(
1324 CreateWebURLLoader(document, gurl));
1325 blink::WebURLRequest url_request = CreateWebURLRequest(document, gurl);
1327 ProgressEventRateLimiter* tracker = new ProgressEventRateLimiter(instance);
1329 // FileDownloader deletes itself after invoking DownloadNexeCompletion.
1330 FileDownloader* file_downloader = new FileDownloader(
1331 url_loader.Pass(),
1332 target_file.Pass(),
1333 base::Bind(&DownloadNexeCompletion, request, out_file_info),
1334 base::Bind(&ProgressEventRateLimiter::ReportProgress,
1335 base::Owned(tracker), std::string(url)));
1336 file_downloader->Load(url_request);
1339 void DownloadNexeCompletion(const DownloadNexeRequest& request,
1340 PP_NaClFileInfo* out_file_info,
1341 FileDownloader::Status status,
1342 base::File target_file,
1343 int http_status) {
1344 int32_t pp_error = FileDownloaderToPepperError(status);
1345 int64_t bytes_read = -1;
1346 if (pp_error == PP_OK && target_file.IsValid()) {
1347 base::File::Info info;
1348 if (target_file.GetInfo(&info))
1349 bytes_read = info.size;
1352 if (bytes_read == -1) {
1353 target_file.Close();
1354 pp_error = PP_ERROR_FAILED;
1357 base::TimeDelta download_time = base::Time::Now() - request.start_time;
1359 NexeLoadManager* load_manager = GetNexeLoadManager(request.instance);
1360 if (load_manager) {
1361 load_manager->NexeFileDidOpen(pp_error,
1362 target_file,
1363 http_status,
1364 bytes_read,
1365 request.url,
1366 download_time);
1369 if (pp_error == PP_OK && target_file.IsValid())
1370 out_file_info->handle = target_file.TakePlatformFile();
1371 else
1372 out_file_info->handle = PP_kInvalidFileHandle;
1374 request.callback.func(request.callback.user_data, pp_error);
1377 void DownloadFileCompletion(
1378 const DownloadFileCallback& callback,
1379 FileDownloader::Status status,
1380 base::File file,
1381 int http_status) {
1382 int32_t pp_error = FileDownloaderToPepperError(status);
1383 PP_NaClFileInfo file_info;
1384 if (pp_error == PP_OK) {
1385 file_info.handle = file.TakePlatformFile();
1386 file_info.token_lo = 0;
1387 file_info.token_hi = 0;
1388 } else {
1389 file_info = kInvalidNaClFileInfo;
1392 callback.Run(pp_error, file_info);
1395 void DownloadFile(PP_Instance instance,
1396 const std::string& url,
1397 const DownloadFileCallback& callback) {
1398 DCHECK(ppapi::PpapiGlobals::Get()->GetMainThreadMessageLoop()->
1399 BelongsToCurrentThread());
1401 NexeLoadManager* load_manager = GetNexeLoadManager(instance);
1402 DCHECK(load_manager);
1403 if (!load_manager) {
1404 base::ThreadTaskRunnerHandle::Get()->PostTask(
1405 FROM_HERE, base::Bind(callback, static_cast<int32_t>(PP_ERROR_FAILED),
1406 kInvalidNaClFileInfo));
1407 return;
1410 // Handle special PNaCl support files which are installed on the user's
1411 // machine.
1412 if (url.find(kPNaClTranslatorBaseUrl, 0) == 0) {
1413 PP_NaClFileInfo file_info = kInvalidNaClFileInfo;
1414 PP_FileHandle handle = GetReadonlyPnaclFd(url.c_str(),
1415 false /* is_executable */,
1416 &file_info.token_lo,
1417 &file_info.token_hi);
1418 if (handle == PP_kInvalidFileHandle) {
1419 base::ThreadTaskRunnerHandle::Get()->PostTask(
1420 FROM_HERE, base::Bind(callback, static_cast<int32_t>(PP_ERROR_FAILED),
1421 kInvalidNaClFileInfo));
1422 return;
1424 file_info.handle = handle;
1425 base::ThreadTaskRunnerHandle::Get()->PostTask(
1426 FROM_HERE,
1427 base::Bind(callback, static_cast<int32_t>(PP_OK), file_info));
1428 return;
1431 // We have to ensure that this url resolves relative to the plugin base url
1432 // before downloading it.
1433 const GURL& test_gurl = load_manager->plugin_base_url().Resolve(url);
1434 if (!test_gurl.is_valid()) {
1435 base::ThreadTaskRunnerHandle::Get()->PostTask(
1436 FROM_HERE, base::Bind(callback, static_cast<int32_t>(PP_ERROR_FAILED),
1437 kInvalidNaClFileInfo));
1438 return;
1441 // Try the fast path for retrieving the file first.
1442 uint64_t file_token_lo = 0;
1443 uint64_t file_token_hi = 0;
1444 PP_FileHandle file_handle = OpenNaClExecutable(instance,
1445 url.c_str(),
1446 &file_token_lo,
1447 &file_token_hi);
1448 if (file_handle != PP_kInvalidFileHandle) {
1449 PP_NaClFileInfo file_info;
1450 file_info.handle = file_handle;
1451 file_info.token_lo = file_token_lo;
1452 file_info.token_hi = file_token_hi;
1453 base::ThreadTaskRunnerHandle::Get()->PostTask(
1454 FROM_HERE,
1455 base::Bind(callback, static_cast<int32_t>(PP_OK), file_info));
1456 return;
1459 // The fast path didn't work, we'll fetch the file using URLLoader and write
1460 // it to local storage.
1461 base::File target_file(CreateTemporaryFile(instance));
1462 GURL gurl(url);
1464 content::PepperPluginInstance* plugin_instance =
1465 content::PepperPluginInstance::Get(instance);
1466 if (!plugin_instance) {
1467 base::ThreadTaskRunnerHandle::Get()->PostTask(
1468 FROM_HERE, base::Bind(callback, static_cast<int32_t>(PP_ERROR_FAILED),
1469 kInvalidNaClFileInfo));
1471 const blink::WebDocument& document =
1472 plugin_instance->GetContainer()->element().document();
1473 scoped_ptr<blink::WebURLLoader> url_loader(
1474 CreateWebURLLoader(document, gurl));
1475 blink::WebURLRequest url_request = CreateWebURLRequest(document, gurl);
1477 ProgressEventRateLimiter* tracker = new ProgressEventRateLimiter(instance);
1479 // FileDownloader deletes itself after invoking DownloadNexeCompletion.
1480 FileDownloader* file_downloader = new FileDownloader(
1481 url_loader.Pass(),
1482 target_file.Pass(),
1483 base::Bind(&DownloadFileCompletion, callback),
1484 base::Bind(&ProgressEventRateLimiter::ReportProgress,
1485 base::Owned(tracker), std::string(url)));
1486 file_downloader->Load(url_request);
1489 void LogTranslateTime(const char* histogram_name,
1490 int64_t time_in_us) {
1491 ppapi::PpapiGlobals::Get()->GetMainThreadMessageLoop()->PostTask(
1492 FROM_HERE,
1493 base::Bind(&HistogramTimeTranslation,
1494 std::string(histogram_name),
1495 time_in_us / 1000));
1498 void LogBytesCompiledVsDowloaded(PP_Bool use_subzero,
1499 int64_t pexe_bytes_compiled,
1500 int64_t pexe_bytes_downloaded) {
1501 HistogramRatio("NaCl.Perf.PNaClLoadTime.PctCompiledWhenFullyDownloaded",
1502 pexe_bytes_compiled, pexe_bytes_downloaded);
1503 HistogramRatio(
1504 use_subzero
1505 ? "NaCl.Perf.PNaClLoadTime.PctCompiledWhenFullyDownloaded.Subzero"
1506 : "NaCl.Perf.PNaClLoadTime.PctCompiledWhenFullyDownloaded.LLC",
1507 pexe_bytes_compiled, pexe_bytes_downloaded);
1510 void SetPNaClStartTime(PP_Instance instance) {
1511 NexeLoadManager* load_manager = GetNexeLoadManager(instance);
1512 if (load_manager)
1513 load_manager->set_pnacl_start_time(base::Time::Now());
1516 // PexeDownloader is responsible for deleting itself when the download
1517 // finishes.
1518 class PexeDownloader : public blink::WebURLLoaderClient {
1519 public:
1520 PexeDownloader(PP_Instance instance,
1521 scoped_ptr<blink::WebURLLoader> url_loader,
1522 const std::string& pexe_url,
1523 int32_t pexe_opt_level,
1524 bool use_subzero,
1525 const PPP_PexeStreamHandler* stream_handler,
1526 void* stream_handler_user_data)
1527 : instance_(instance),
1528 url_loader_(url_loader.Pass()),
1529 pexe_url_(pexe_url),
1530 pexe_opt_level_(pexe_opt_level),
1531 use_subzero_(use_subzero),
1532 stream_handler_(stream_handler),
1533 stream_handler_user_data_(stream_handler_user_data),
1534 success_(false),
1535 expected_content_length_(-1),
1536 weak_factory_(this) {}
1538 void Load(const blink::WebURLRequest& request) {
1539 url_loader_->loadAsynchronously(request, this);
1542 private:
1543 virtual void didReceiveResponse(blink::WebURLLoader* loader,
1544 const blink::WebURLResponse& response) {
1545 success_ = (response.httpStatusCode() == 200);
1546 if (!success_)
1547 return;
1549 expected_content_length_ = response.expectedContentLength();
1551 // Defer loading after receiving headers. This is because we may already
1552 // have a cached translated nexe, so check for that now.
1553 url_loader_->setDefersLoading(true);
1555 std::string etag = response.httpHeaderField("etag").utf8();
1556 std::string last_modified =
1557 response.httpHeaderField("last-modified").utf8();
1558 base::Time last_modified_time;
1559 base::Time::FromString(last_modified.c_str(), &last_modified_time);
1561 bool has_no_store_header = false;
1562 std::string cache_control =
1563 response.httpHeaderField("cache-control").utf8();
1565 std::vector<std::string> values;
1566 base::SplitString(cache_control, ',', &values);
1567 for (std::vector<std::string>::const_iterator it = values.begin();
1568 it != values.end();
1569 ++it) {
1570 if (base::StringToLowerASCII(*it) == "no-store")
1571 has_no_store_header = true;
1574 GetNexeFd(
1575 instance_, pexe_url_, pexe_opt_level_, last_modified_time, etag,
1576 has_no_store_header, use_subzero_,
1577 base::Bind(&PexeDownloader::didGetNexeFd, weak_factory_.GetWeakPtr()));
1580 virtual void didGetNexeFd(int32_t pp_error,
1581 bool cache_hit,
1582 PP_FileHandle file_handle) {
1583 if (!content::PepperPluginInstance::Get(instance_)) {
1584 delete this;
1585 return;
1588 HistogramEnumerate("NaCl.Perf.PNaClCache.IsHit", cache_hit, 2);
1589 HistogramEnumerate(use_subzero_ ? "NaCl.Perf.PNaClCache.IsHit.Subzero"
1590 : "NaCl.Perf.PNaClCache.IsHit.LLC",
1591 cache_hit, 2);
1592 if (cache_hit) {
1593 stream_handler_->DidCacheHit(stream_handler_user_data_, file_handle);
1595 // We delete the PexeDownloader at this point since we successfully got a
1596 // cached, translated nexe.
1597 delete this;
1598 return;
1600 stream_handler_->DidCacheMiss(stream_handler_user_data_,
1601 expected_content_length_,
1602 file_handle);
1604 // No translated nexe was found in the cache, so we should download the
1605 // file to start streaming it.
1606 url_loader_->setDefersLoading(false);
1609 virtual void didReceiveData(blink::WebURLLoader* loader,
1610 const char* data,
1611 int data_length,
1612 int encoded_data_length) {
1613 if (content::PepperPluginInstance::Get(instance_)) {
1614 // Stream the data we received to the stream callback.
1615 stream_handler_->DidStreamData(stream_handler_user_data_,
1616 data,
1617 data_length);
1621 virtual void didFinishLoading(blink::WebURLLoader* loader,
1622 double finish_time,
1623 int64_t total_encoded_data_length) {
1624 int32_t result = success_ ? PP_OK : PP_ERROR_FAILED;
1626 if (content::PepperPluginInstance::Get(instance_))
1627 stream_handler_->DidFinishStream(stream_handler_user_data_, result);
1628 delete this;
1631 virtual void didFail(blink::WebURLLoader* loader,
1632 const blink::WebURLError& error) {
1633 success_ = false;
1636 PP_Instance instance_;
1637 scoped_ptr<blink::WebURLLoader> url_loader_;
1638 std::string pexe_url_;
1639 int32_t pexe_opt_level_;
1640 bool use_subzero_;
1641 const PPP_PexeStreamHandler* stream_handler_;
1642 void* stream_handler_user_data_;
1643 bool success_;
1644 int64_t expected_content_length_;
1645 base::WeakPtrFactory<PexeDownloader> weak_factory_;
1648 void StreamPexe(PP_Instance instance,
1649 const char* pexe_url,
1650 int32_t opt_level,
1651 PP_Bool use_subzero,
1652 const PPP_PexeStreamHandler* handler,
1653 void* handler_user_data) {
1654 content::PepperPluginInstance* plugin_instance =
1655 content::PepperPluginInstance::Get(instance);
1656 if (!plugin_instance) {
1657 base::ThreadTaskRunnerHandle::Get()->PostTask(
1658 FROM_HERE, base::Bind(handler->DidFinishStream, handler_user_data,
1659 static_cast<int32_t>(PP_ERROR_FAILED)));
1660 return;
1663 GURL gurl(pexe_url);
1664 const blink::WebDocument& document =
1665 plugin_instance->GetContainer()->element().document();
1666 scoped_ptr<blink::WebURLLoader> url_loader(
1667 CreateWebURLLoader(document, gurl));
1668 PexeDownloader* downloader =
1669 new PexeDownloader(instance, url_loader.Pass(), pexe_url, opt_level,
1670 PP_ToBool(use_subzero), handler, handler_user_data);
1672 blink::WebURLRequest url_request = CreateWebURLRequest(document, gurl);
1673 // Mark the request as requesting a PNaCl bitcode file,
1674 // so that component updater can detect this user action.
1675 url_request.addHTTPHeaderField(
1676 blink::WebString::fromUTF8("Accept"),
1677 blink::WebString::fromUTF8("application/x-pnacl, */*"));
1678 url_request.setRequestContext(blink::WebURLRequest::RequestContextObject);
1679 downloader->Load(url_request);
1682 const PPB_NaCl_Private nacl_interface = {
1683 &LaunchSelLdr,
1684 &UrandomFD,
1685 &BrokerDuplicateHandle,
1686 &GetReadExecPnaclFd,
1687 &CreateTemporaryFile,
1688 &GetNumberOfProcessors,
1689 &ReportTranslationFinished,
1690 &DispatchEvent,
1691 &ReportLoadError,
1692 &InstanceCreated,
1693 &InstanceDestroyed,
1694 &GetSandboxArch,
1695 &Vlog,
1696 &InitializePlugin,
1697 &RequestNaClManifest,
1698 &GetManifestBaseURL,
1699 &ProcessNaClManifest,
1700 &ManifestGetProgramURL,
1701 &GetPNaClResourceInfo,
1702 &GetCpuFeatureAttrs,
1703 &DownloadNexe,
1704 &LogTranslateTime,
1705 &LogBytesCompiledVsDowloaded,
1706 &SetPNaClStartTime,
1707 &StreamPexe
1710 } // namespace
1712 const PPB_NaCl_Private* GetNaClPrivateInterface() {
1713 return &nacl_interface;
1716 } // namespace nacl