Roll src/third_party/WebKit f36d5e0:68b67cd (svn 193299:193303)
[chromium-blink-merge.git] / components / nacl / renderer / ppb_nacl_private_impl.cc
blobe91c8ff967a6fdb092f80b48274b594390fc71d3
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/lazy_instance.h"
18 #include "base/logging.h"
19 #include "base/rand_util.h"
20 #include "base/strings/string_split.h"
21 #include "base/strings/string_util.h"
22 #include "components/nacl/common/nacl_host_messages.h"
23 #include "components/nacl/common/nacl_messages.h"
24 #include "components/nacl/common/nacl_nonsfi_util.h"
25 #include "components/nacl/common/nacl_switches.h"
26 #include "components/nacl/common/nacl_types.h"
27 #include "components/nacl/renderer/file_downloader.h"
28 #include "components/nacl/renderer/histogram.h"
29 #include "components/nacl/renderer/json_manifest.h"
30 #include "components/nacl/renderer/manifest_downloader.h"
31 #include "components/nacl/renderer/manifest_service_channel.h"
32 #include "components/nacl/renderer/nexe_load_manager.h"
33 #include "components/nacl/renderer/platform_info.h"
34 #include "components/nacl/renderer/pnacl_translation_resource_host.h"
35 #include "components/nacl/renderer/progress_event.h"
36 #include "components/nacl/renderer/trusted_plugin_channel.h"
37 #include "content/public/common/content_client.h"
38 #include "content/public/common/content_switches.h"
39 #include "content/public/common/sandbox_init.h"
40 #include "content/public/renderer/pepper_plugin_instance.h"
41 #include "content/public/renderer/render_thread.h"
42 #include "content/public/renderer/render_view.h"
43 #include "content/public/renderer/renderer_ppapi_host.h"
44 #include "native_client/src/public/imc_types.h"
45 #include "net/base/data_url.h"
46 #include "net/base/net_errors.h"
47 #include "net/http/http_util.h"
48 #include "ppapi/c/pp_bool.h"
49 #include "ppapi/c/private/pp_file_handle.h"
50 #include "ppapi/shared_impl/ppapi_globals.h"
51 #include "ppapi/shared_impl/ppapi_permissions.h"
52 #include "ppapi/shared_impl/ppapi_preferences.h"
53 #include "ppapi/shared_impl/var.h"
54 #include "ppapi/shared_impl/var_tracker.h"
55 #include "ppapi/thunk/enter.h"
56 #include "third_party/WebKit/public/platform/WebURLLoader.h"
57 #include "third_party/WebKit/public/platform/WebURLResponse.h"
58 #include "third_party/WebKit/public/web/WebDocument.h"
59 #include "third_party/WebKit/public/web/WebElement.h"
60 #include "third_party/WebKit/public/web/WebLocalFrame.h"
61 #include "third_party/WebKit/public/web/WebPluginContainer.h"
62 #include "third_party/WebKit/public/web/WebSecurityOrigin.h"
63 #include "third_party/WebKit/public/web/WebURLLoaderOptions.h"
64 #include "third_party/jsoncpp/source/include/json/reader.h"
65 #include "third_party/jsoncpp/source/include/json/value.h"
67 namespace nacl {
68 namespace {
70 // The pseudo-architecture used to indicate portable native client.
71 const char* const kPortableArch = "portable";
73 // The base URL for resources used by the PNaCl translator processes.
74 const char* kPNaClTranslatorBaseUrl = "chrome://pnacl-translator/";
76 base::LazyInstance<scoped_refptr<PnaclTranslationResourceHost> >
77 g_pnacl_resource_host = LAZY_INSTANCE_INITIALIZER;
79 bool InitializePnaclResourceHost() {
80 // Must run on the main thread.
81 content::RenderThread* render_thread = content::RenderThread::Get();
82 if (!render_thread)
83 return false;
84 if (!g_pnacl_resource_host.Get().get()) {
85 g_pnacl_resource_host.Get() = new PnaclTranslationResourceHost(
86 render_thread->GetIOMessageLoopProxy());
87 render_thread->AddFilter(g_pnacl_resource_host.Get().get());
89 return true;
92 bool CanOpenViaFastPath(content::PepperPluginInstance* plugin_instance,
93 const GURL& gurl) {
94 // Fast path only works for installed file URLs.
95 if (!gurl.SchemeIs("chrome-extension"))
96 return PP_kInvalidFileHandle;
98 // IMPORTANT: Make sure the document can request the given URL. If we don't
99 // check, a malicious app could probe the extension system. This enforces a
100 // same-origin policy which prevents the app from requesting resources from
101 // another app.
102 blink::WebSecurityOrigin security_origin =
103 plugin_instance->GetContainer()->element().document().securityOrigin();
104 return security_origin.canRequest(gurl);
107 // This contains state that is produced by LaunchSelLdr() and consumed
108 // by StartPpapiProxy().
109 struct InstanceInfo {
110 InstanceInfo() : plugin_pid(base::kNullProcessId), plugin_child_id(0) {}
111 GURL url;
112 ppapi::PpapiPermissions permissions;
113 base::ProcessId plugin_pid;
114 int plugin_child_id;
115 IPC::ChannelHandle channel_handle;
118 class NaClPluginInstance {
119 public:
120 NaClPluginInstance(PP_Instance instance):
121 nexe_load_manager(instance), pexe_size(0) {}
123 NexeLoadManager nexe_load_manager;
124 scoped_ptr<JsonManifest> json_manifest;
125 scoped_ptr<InstanceInfo> instance_info;
127 // When translation is complete, this records the size of the pexe in
128 // bytes so that it can be reported in a later load event.
129 uint64_t pexe_size;
132 typedef base::ScopedPtrHashMap<PP_Instance, NaClPluginInstance> InstanceMap;
133 base::LazyInstance<InstanceMap> g_instance_map = LAZY_INSTANCE_INITIALIZER;
135 NaClPluginInstance* GetNaClPluginInstance(PP_Instance instance) {
136 InstanceMap& map = g_instance_map.Get();
137 InstanceMap::iterator iter = map.find(instance);
138 if (iter == map.end())
139 return NULL;
140 return iter->second;
143 NexeLoadManager* GetNexeLoadManager(PP_Instance instance) {
144 NaClPluginInstance* nacl_plugin_instance = GetNaClPluginInstance(instance);
145 if (!nacl_plugin_instance)
146 return NULL;
147 return &nacl_plugin_instance->nexe_load_manager;
150 JsonManifest* GetJsonManifest(PP_Instance instance) {
151 NaClPluginInstance* nacl_plugin_instance = GetNaClPluginInstance(instance);
152 if (!nacl_plugin_instance)
153 return NULL;
154 return nacl_plugin_instance->json_manifest.get();
157 static const PP_NaClFileInfo kInvalidNaClFileInfo = {
158 PP_kInvalidFileHandle,
159 0, // token_lo
160 0, // token_hi
163 int GetRoutingID(PP_Instance instance) {
164 // Check that we are on the main renderer thread.
165 DCHECK(content::RenderThread::Get());
166 content::RendererPpapiHost* host =
167 content::RendererPpapiHost::GetForPPInstance(instance);
168 if (!host)
169 return 0;
170 return host->GetRoutingIDForWidget(instance);
173 // Returns whether the channel_handle is valid or not.
174 bool IsValidChannelHandle(const IPC::ChannelHandle& channel_handle) {
175 if (channel_handle.name.empty()) {
176 return false;
179 #if defined(OS_POSIX)
180 if (channel_handle.socket.fd == -1) {
181 return false;
183 #endif
185 return true;
188 void PostPPCompletionCallback(PP_CompletionCallback callback,
189 int32_t status) {
190 ppapi::PpapiGlobals::Get()->GetMainThreadMessageLoop()->PostTask(
191 FROM_HERE,
192 base::Bind(callback.func, callback.user_data, status));
195 bool ManifestResolveKey(PP_Instance instance,
196 bool is_helper_process,
197 const std::string& key,
198 std::string* full_url,
199 PP_PNaClOptions* pnacl_options);
201 typedef base::Callback<void(int32_t, const PP_NaClFileInfo&)>
202 DownloadFileCallback;
204 void DownloadFile(PP_Instance instance,
205 const std::string& url,
206 const DownloadFileCallback& callback);
208 PP_Bool StartPpapiProxy(PP_Instance instance);
210 // Thin adapter from PPP_ManifestService to ManifestServiceChannel::Delegate.
211 // Note that user_data is managed by the caller of LaunchSelLdr. Please see
212 // also PP_ManifestService's comment for more details about resource
213 // management.
214 class ManifestServiceProxy : public ManifestServiceChannel::Delegate {
215 public:
216 ManifestServiceProxy(PP_Instance pp_instance, NaClAppProcessType process_type)
217 : pp_instance_(pp_instance), process_type_(process_type) {}
219 ~ManifestServiceProxy() override {}
221 void StartupInitializationComplete() override {
222 if (StartPpapiProxy(pp_instance_) == PP_TRUE) {
223 NaClPluginInstance* nacl_plugin_instance =
224 GetNaClPluginInstance(pp_instance_);
225 JsonManifest* manifest = GetJsonManifest(pp_instance_);
226 if (nacl_plugin_instance && manifest) {
227 NexeLoadManager* load_manager =
228 &nacl_plugin_instance->nexe_load_manager;
229 std::string full_url;
230 PP_PNaClOptions pnacl_options;
231 bool uses_nonsfi_mode;
232 JsonManifest::ErrorInfo error_info;
233 if (manifest->GetProgramURL(&full_url,
234 &pnacl_options,
235 &uses_nonsfi_mode,
236 &error_info)) {
237 int64_t exe_size = nacl_plugin_instance->pexe_size;
238 if (exe_size == 0)
239 exe_size = load_manager->nexe_size();
240 load_manager->ReportLoadSuccess(full_url, exe_size, exe_size);
246 void OpenResource(
247 const std::string& key,
248 const ManifestServiceChannel::OpenResourceCallback& callback) override {
249 DCHECK(ppapi::PpapiGlobals::Get()->GetMainThreadMessageLoop()->
250 BelongsToCurrentThread());
252 // For security hardening, disable open_resource() when it is isn't
253 // needed. PNaCl pexes can't use open_resource(), but general nexes
254 // and the PNaCl translator nexes may use it.
255 if (process_type_ != kNativeNaClProcessType &&
256 process_type_ != kPNaClTranslatorProcessType) {
257 // Return an error.
258 base::MessageLoop::current()->PostTask(
259 FROM_HERE,
260 base::Bind(callback, base::Passed(base::File()), 0, 0));
261 return;
264 std::string url;
265 // TODO(teravest): Clean up pnacl_options logic in JsonManifest so we don't
266 // have to initialize it like this here.
267 PP_PNaClOptions pnacl_options;
268 pnacl_options.translate = PP_FALSE;
269 pnacl_options.is_debug = PP_FALSE;
270 pnacl_options.use_subzero = PP_FALSE;
271 pnacl_options.opt_level = 2;
272 bool is_helper_process = process_type_ == kPNaClTranslatorProcessType;
273 if (!ManifestResolveKey(pp_instance_, is_helper_process, key, &url,
274 &pnacl_options)) {
275 base::MessageLoop::current()->PostTask(
276 FROM_HERE,
277 base::Bind(callback, base::Passed(base::File()), 0, 0));
278 return;
281 // We have to call DidDownloadFile, even if this object is destroyed, so
282 // that the handle inside PP_NaClFileInfo isn't leaked. This means that the
283 // callback passed to this function shouldn't have a weak pointer to an
284 // object either.
286 // TODO(teravest): Make a type like PP_NaClFileInfo to use for DownloadFile
287 // that would close the file handle on destruction.
288 DownloadFile(pp_instance_, url,
289 base::Bind(&ManifestServiceProxy::DidDownloadFile, callback));
292 private:
293 static void DidDownloadFile(
294 ManifestServiceChannel::OpenResourceCallback callback,
295 int32_t pp_error,
296 const PP_NaClFileInfo& file_info) {
297 if (pp_error != PP_OK) {
298 callback.Run(base::File(), 0, 0);
299 return;
301 callback.Run(base::File(file_info.handle),
302 file_info.token_lo,
303 file_info.token_hi);
306 PP_Instance pp_instance_;
307 NaClAppProcessType process_type_;
308 DISALLOW_COPY_AND_ASSIGN(ManifestServiceProxy);
311 blink::WebURLLoader* CreateWebURLLoader(const blink::WebDocument& document,
312 const GURL& gurl) {
313 blink::WebURLLoaderOptions options;
314 options.untrustedHTTP = true;
316 // Options settings here follow the original behavior in the trusted
317 // plugin and PepperURLLoaderHost.
318 if (document.securityOrigin().canRequest(gurl)) {
319 options.allowCredentials = true;
320 } else {
321 // Allow CORS.
322 options.crossOriginRequestPolicy =
323 blink::WebURLLoaderOptions::CrossOriginRequestPolicyUseAccessControl;
325 return document.frame()->createAssociatedURLLoader(options);
328 blink::WebURLRequest CreateWebURLRequest(const blink::WebDocument& document,
329 const GURL& gurl) {
330 blink::WebURLRequest request;
331 request.initialize();
332 request.setURL(gurl);
333 request.setFirstPartyForCookies(document.firstPartyForCookies());
334 return request;
337 int32_t FileDownloaderToPepperError(FileDownloader::Status status) {
338 switch (status) {
339 case FileDownloader::SUCCESS:
340 return PP_OK;
341 case FileDownloader::ACCESS_DENIED:
342 return PP_ERROR_NOACCESS;
343 case FileDownloader::FAILED:
344 return PP_ERROR_FAILED;
345 // No default case, to catch unhandled Status values.
347 return PP_ERROR_FAILED;
350 NaClAppProcessType PP_ToNaClAppProcessType(
351 PP_NaClAppProcessType pp_process_type) {
352 #define STATICALLY_CHECK_NACLAPPPROCESSTYPE_EQ(pp, nonpp) \
353 static_assert(static_cast<int>(pp) == static_cast<int>(nonpp), \
354 "PP_NaClAppProcessType differs from NaClAppProcessType");
355 STATICALLY_CHECK_NACLAPPPROCESSTYPE_EQ(PP_UNKNOWN_NACL_PROCESS_TYPE,
356 kUnknownNaClProcessType);
357 STATICALLY_CHECK_NACLAPPPROCESSTYPE_EQ(PP_NATIVE_NACL_PROCESS_TYPE,
358 kNativeNaClProcessType);
359 STATICALLY_CHECK_NACLAPPPROCESSTYPE_EQ(PP_PNACL_PROCESS_TYPE,
360 kPNaClProcessType);
361 STATICALLY_CHECK_NACLAPPPROCESSTYPE_EQ(PP_PNACL_TRANSLATOR_PROCESS_TYPE,
362 kPNaClTranslatorProcessType);
363 STATICALLY_CHECK_NACLAPPPROCESSTYPE_EQ(PP_NUM_NACL_PROCESS_TYPES,
364 kNumNaClProcessTypes);
365 #undef STATICALLY_CHECK_NACLAPPPROCESSTYPE_EQ
366 DCHECK(pp_process_type > PP_UNKNOWN_NACL_PROCESS_TYPE &&
367 pp_process_type < PP_NUM_NACL_PROCESS_TYPES);
368 return static_cast<NaClAppProcessType>(pp_process_type);
371 // Launch NaCl's sel_ldr process.
372 void LaunchSelLdr(PP_Instance instance,
373 PP_Bool main_service_runtime,
374 const char* alleged_url,
375 const PP_NaClFileInfo* nexe_file_info,
376 PP_Bool uses_nonsfi_mode,
377 PP_NaClAppProcessType pp_process_type,
378 void* imc_handle,
379 PP_CompletionCallback callback) {
380 CHECK(ppapi::PpapiGlobals::Get()->GetMainThreadMessageLoop()->
381 BelongsToCurrentThread());
382 NaClAppProcessType process_type = PP_ToNaClAppProcessType(pp_process_type);
383 // Create the manifest service proxy here, so on error case, it will be
384 // destructed (without passing it to ManifestServiceChannel).
385 scoped_ptr<ManifestServiceChannel::Delegate> manifest_service_proxy(
386 new ManifestServiceProxy(instance, process_type));
388 FileDescriptor result_socket;
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);
418 std::string error_message_string;
419 NaClLaunchResult launch_result;
421 IPC::PlatformFileForTransit nexe_for_transit =
422 IPC::InvalidPlatformFileForTransit();
424 std::vector<std::pair<
425 std::string /*key*/, std::string /*url*/> > resource_files_to_prefetch;
426 if (process_type == kNativeNaClProcessType && uses_nonsfi_mode) {
427 JsonManifest* manifest = GetJsonManifest(instance);
428 if (manifest)
429 manifest->GetPrefetchableFiles(&resource_files_to_prefetch);
430 for (size_t i = 0; i < resource_files_to_prefetch.size(); ++i) {
431 const GURL gurl(resource_files_to_prefetch[i].second);
432 // Important security check. Do not remove.
433 if (!CanOpenViaFastPath(plugin_instance, gurl)) {
434 resource_files_to_prefetch.clear();
435 break;
440 #if defined(OS_POSIX)
441 if (nexe_file_info->handle != PP_kInvalidFileHandle)
442 nexe_for_transit = base::FileDescriptor(nexe_file_info->handle, true);
443 #elif defined(OS_WIN)
444 // Duplicate the handle on the browser side instead of the renderer.
445 // This is because BrokerGetFileForProcess isn't part of content/public, and
446 // it's simpler to do the duplication in the browser anyway.
447 nexe_for_transit = nexe_file_info->handle;
448 #else
449 #error Unsupported target platform.
450 #endif
451 if (!sender->Send(new NaClHostMsg_LaunchNaCl(
452 NaClLaunchParams(
453 instance_info.url.spec(),
454 nexe_for_transit,
455 nexe_file_info->token_lo,
456 nexe_file_info->token_hi,
457 resource_files_to_prefetch,
458 routing_id,
459 perm_bits,
460 PP_ToBool(uses_nonsfi_mode),
461 process_type),
462 &launch_result,
463 &error_message_string))) {
464 ppapi::PpapiGlobals::Get()->GetMainThreadMessageLoop()->PostTask(
465 FROM_HERE,
466 base::Bind(callback.func, callback.user_data,
467 static_cast<int32_t>(PP_ERROR_FAILED)));
468 return;
471 load_manager->set_nonsfi(PP_ToBool(uses_nonsfi_mode));
473 if (!error_message_string.empty()) {
474 if (PP_ToBool(main_service_runtime)) {
475 load_manager->ReportLoadError(PP_NACL_ERROR_SEL_LDR_LAUNCH,
476 "ServiceRuntime: failed to start",
477 error_message_string);
479 ppapi::PpapiGlobals::Get()->GetMainThreadMessageLoop()->PostTask(
480 FROM_HERE,
481 base::Bind(callback.func, callback.user_data,
482 static_cast<int32_t>(PP_ERROR_FAILED)));
483 return;
485 result_socket = launch_result.imc_channel_handle;
486 instance_info.channel_handle = launch_result.ppapi_ipc_channel_handle;
487 instance_info.plugin_pid = launch_result.plugin_pid;
488 instance_info.plugin_child_id = launch_result.plugin_child_id;
490 // Don't save instance_info if channel handle is invalid.
491 if (IsValidChannelHandle(instance_info.channel_handle)) {
492 NaClPluginInstance* nacl_plugin_instance = GetNaClPluginInstance(instance);
493 nacl_plugin_instance->instance_info.reset(new InstanceInfo(instance_info));
496 *(static_cast<NaClHandle*>(imc_handle)) = ToNativeHandle(result_socket);
498 // Store the crash information shared memory handle.
499 load_manager->set_crash_info_shmem_handle(
500 launch_result.crash_info_shmem_handle);
502 // Create the trusted plugin channel.
503 if (IsValidChannelHandle(launch_result.trusted_ipc_channel_handle)) {
504 bool report_exit_status = PP_ToBool(main_service_runtime);
505 scoped_ptr<TrustedPluginChannel> trusted_plugin_channel(
506 new TrustedPluginChannel(
507 load_manager,
508 launch_result.trusted_ipc_channel_handle,
509 content::RenderThread::Get()->GetShutdownEvent(),
510 report_exit_status));
511 load_manager->set_trusted_plugin_channel(trusted_plugin_channel.Pass());
512 } else {
513 PostPPCompletionCallback(callback, PP_ERROR_FAILED);
514 return;
517 // Create the manifest service handle as well.
518 if (IsValidChannelHandle(launch_result.manifest_service_ipc_channel_handle)) {
519 scoped_ptr<ManifestServiceChannel> manifest_service_channel(
520 new ManifestServiceChannel(
521 launch_result.manifest_service_ipc_channel_handle,
522 base::Bind(&PostPPCompletionCallback, callback),
523 manifest_service_proxy.Pass(),
524 content::RenderThread::Get()->GetShutdownEvent()));
525 load_manager->set_manifest_service_channel(
526 manifest_service_channel.Pass());
530 PP_Bool StartPpapiProxy(PP_Instance instance) {
531 NexeLoadManager* load_manager = GetNexeLoadManager(instance);
532 DCHECK(load_manager);
533 if (!load_manager)
534 return PP_FALSE;
536 content::PepperPluginInstance* plugin_instance =
537 content::PepperPluginInstance::Get(instance);
538 if (!plugin_instance) {
539 DLOG(ERROR) << "GetInstance() failed";
540 return PP_FALSE;
543 NaClPluginInstance* nacl_plugin_instance = GetNaClPluginInstance(instance);
544 if (!nacl_plugin_instance->instance_info) {
545 DLOG(ERROR) << "Could not find instance ID";
546 return PP_FALSE;
548 scoped_ptr<InstanceInfo> instance_info =
549 nacl_plugin_instance->instance_info.Pass();
551 PP_ExternalPluginResult result = plugin_instance->SwitchToOutOfProcessProxy(
552 base::FilePath().AppendASCII(instance_info->url.spec()),
553 instance_info->permissions,
554 instance_info->channel_handle,
555 instance_info->plugin_pid,
556 instance_info->plugin_child_id);
558 if (result == PP_EXTERNAL_PLUGIN_OK) {
559 // Log the amound of time that has passed between the trusted plugin being
560 // initialized and the untrusted plugin being initialized. This is
561 // (roughly) the cost of using NaCl, in terms of startup time.
562 load_manager->ReportStartupOverhead();
563 return PP_TRUE;
564 } else if (result == PP_EXTERNAL_PLUGIN_ERROR_MODULE) {
565 load_manager->ReportLoadError(PP_NACL_ERROR_START_PROXY_MODULE,
566 "could not initialize module.");
567 } else if (result == PP_EXTERNAL_PLUGIN_ERROR_INSTANCE) {
568 load_manager->ReportLoadError(PP_NACL_ERROR_START_PROXY_MODULE,
569 "could not create instance.");
571 return PP_FALSE;
574 int UrandomFD(void) {
575 #if defined(OS_POSIX)
576 return base::GetUrandomFD();
577 #else
578 return -1;
579 #endif
582 int32_t BrokerDuplicateHandle(PP_FileHandle source_handle,
583 uint32_t process_id,
584 PP_FileHandle* target_handle,
585 uint32_t desired_access,
586 uint32_t options) {
587 #if defined(OS_WIN)
588 return content::BrokerDuplicateHandle(source_handle, process_id,
589 target_handle, desired_access,
590 options);
591 #else
592 return 0;
593 #endif
596 // Convert a URL to a filename for GetReadonlyPnaclFd.
597 // Must be kept in sync with PnaclCanOpenFile() in
598 // components/nacl/browser/nacl_file_host.cc.
599 std::string PnaclComponentURLToFilename(const std::string& url) {
600 // PNaCl component URLs aren't arbitrary URLs; they are always either
601 // generated from ManifestResolveKey or PnaclResources::ReadResourceInfo.
602 // So, it's safe to just use string parsing operations here instead of
603 // URL-parsing ones.
604 DCHECK(StartsWithASCII(url, kPNaClTranslatorBaseUrl, true));
605 std::string r = url.substr(std::string(kPNaClTranslatorBaseUrl).length());
607 // Use white-listed-chars.
608 size_t replace_pos;
609 static const char* white_list = "abcdefghijklmnopqrstuvwxyz0123456789_";
610 replace_pos = r.find_first_not_of(white_list);
611 while(replace_pos != std::string::npos) {
612 r = r.replace(replace_pos, 1, "_");
613 replace_pos = r.find_first_not_of(white_list);
615 return r;
618 PP_FileHandle GetReadonlyPnaclFd(const char* url,
619 bool is_executable,
620 uint64_t* nonce_lo,
621 uint64_t* nonce_hi) {
622 std::string filename = PnaclComponentURLToFilename(url);
623 IPC::PlatformFileForTransit out_fd = IPC::InvalidPlatformFileForTransit();
624 IPC::Sender* sender = content::RenderThread::Get();
625 DCHECK(sender);
626 if (!sender->Send(new NaClHostMsg_GetReadonlyPnaclFD(
627 std::string(filename), is_executable,
628 &out_fd, nonce_lo, nonce_hi))) {
629 return PP_kInvalidFileHandle;
631 if (out_fd == IPC::InvalidPlatformFileForTransit()) {
632 return PP_kInvalidFileHandle;
634 return IPC::PlatformFileForTransitToPlatformFile(out_fd);
637 void GetReadExecPnaclFd(const char* url,
638 PP_NaClFileInfo* out_file_info) {
639 *out_file_info = kInvalidNaClFileInfo;
640 out_file_info->handle = GetReadonlyPnaclFd(url, true /* is_executable */,
641 &out_file_info->token_lo,
642 &out_file_info->token_hi);
645 PP_FileHandle CreateTemporaryFile(PP_Instance instance) {
646 IPC::PlatformFileForTransit transit_fd = IPC::InvalidPlatformFileForTransit();
647 IPC::Sender* sender = content::RenderThread::Get();
648 DCHECK(sender);
649 if (!sender->Send(new NaClHostMsg_NaClCreateTemporaryFile(
650 &transit_fd))) {
651 return PP_kInvalidFileHandle;
654 if (transit_fd == IPC::InvalidPlatformFileForTransit()) {
655 return PP_kInvalidFileHandle;
658 return IPC::PlatformFileForTransitToPlatformFile(transit_fd);
661 int32_t GetNumberOfProcessors() {
662 IPC::Sender* sender = content::RenderThread::Get();
663 DCHECK(sender);
664 int32_t num_processors = 1;
665 return sender->Send(new NaClHostMsg_NaClGetNumProcessors(&num_processors)) ?
666 num_processors : 1;
669 void GetNexeFd(PP_Instance instance,
670 const std::string& pexe_url,
671 uint32_t opt_level,
672 const base::Time& last_modified_time,
673 const std::string& etag,
674 bool has_no_store_header,
675 bool use_subzero,
676 base::Callback<void(int32_t, bool, PP_FileHandle)> callback) {
677 if (!InitializePnaclResourceHost()) {
678 ppapi::PpapiGlobals::Get()->GetMainThreadMessageLoop()->PostTask(
679 FROM_HERE,
680 base::Bind(callback,
681 static_cast<int32_t>(PP_ERROR_FAILED),
682 false,
683 PP_kInvalidFileHandle));
684 return;
687 PnaclCacheInfo cache_info;
688 cache_info.pexe_url = GURL(pexe_url);
689 // TODO(dschuff): Get this value from the pnacl json file after it
690 // rolls in from NaCl.
691 cache_info.abi_version = 1;
692 cache_info.opt_level = opt_level;
693 cache_info.last_modified = last_modified_time;
694 cache_info.etag = etag;
695 cache_info.has_no_store_header = has_no_store_header;
696 cache_info.use_subzero = use_subzero;
697 cache_info.sandbox_isa = GetSandboxArch();
698 cache_info.extra_flags = GetCpuFeatures();
700 g_pnacl_resource_host.Get()->RequestNexeFd(
701 GetRoutingID(instance),
702 instance,
703 cache_info,
704 callback);
707 void LogTranslationFinishedUMA(const std::string& uma_suffix,
708 int32_t opt_level,
709 int32_t unknown_opt_level,
710 int64_t nexe_size,
711 int64_t pexe_size,
712 int64_t compile_time_us,
713 base::TimeDelta total_time) {
714 HistogramEnumerate("NaCl.Options.PNaCl.OptLevel" + uma_suffix, opt_level,
715 unknown_opt_level + 1);
716 HistogramKBPerSec("NaCl.Perf.PNaClLoadTime.CompileKBPerSec" + uma_suffix,
717 pexe_size / 1024, compile_time_us);
718 HistogramSizeKB("NaCl.Perf.Size.PNaClTranslatedNexe" + uma_suffix,
719 nexe_size / 1024);
720 HistogramSizeKB("NaCl.Perf.Size.Pexe" + uma_suffix, pexe_size / 1024);
721 HistogramRatio("NaCl.Perf.Size.PexeNexeSizePct" + uma_suffix, pexe_size,
722 nexe_size);
723 HistogramTimeTranslation(
724 "NaCl.Perf.PNaClLoadTime.TotalUncachedTime" + uma_suffix,
725 total_time.InMilliseconds());
726 HistogramKBPerSec(
727 "NaCl.Perf.PNaClLoadTime.TotalUncachedKBPerSec" + uma_suffix,
728 pexe_size / 1024, total_time.InMicroseconds());
731 void ReportTranslationFinished(PP_Instance instance,
732 PP_Bool success,
733 int32_t opt_level,
734 PP_Bool use_subzero,
735 int64_t nexe_size,
736 int64_t pexe_size,
737 int64_t compile_time_us) {
738 NexeLoadManager* load_manager = GetNexeLoadManager(instance);
739 DCHECK(load_manager);
740 if (success == PP_TRUE && load_manager) {
741 base::TimeDelta total_time =
742 base::Time::Now() - load_manager->pnacl_start_time();
743 static const int32_t kUnknownOptLevel = 4;
744 if (opt_level < 0 || opt_level > 3)
745 opt_level = kUnknownOptLevel;
746 // Log twice: once to cover all PNaCl UMA, and then a second
747 // time with the more specific UMA (Subzero vs LLC).
748 std::string uma_suffix(use_subzero ? ".Subzero" : ".LLC");
749 LogTranslationFinishedUMA("", opt_level, kUnknownOptLevel, nexe_size,
750 pexe_size, compile_time_us, total_time);
751 LogTranslationFinishedUMA(uma_suffix, opt_level, kUnknownOptLevel,
752 nexe_size, pexe_size, compile_time_us,
753 total_time);
756 // If the resource host isn't initialized, don't try to do that here.
757 // Just return because something is already very wrong.
758 if (g_pnacl_resource_host.Get().get() == NULL)
759 return;
760 g_pnacl_resource_host.Get()->ReportTranslationFinished(instance, success);
762 // Record the pexe size for reporting in a later load event.
763 NaClPluginInstance* nacl_plugin_instance = GetNaClPluginInstance(instance);
764 if (nacl_plugin_instance) {
765 nacl_plugin_instance->pexe_size = pexe_size;
769 PP_FileHandle OpenNaClExecutable(PP_Instance instance,
770 const char* file_url,
771 uint64_t* nonce_lo,
772 uint64_t* nonce_hi) {
773 NexeLoadManager* load_manager = GetNexeLoadManager(instance);
774 DCHECK(load_manager);
775 if (!load_manager)
776 return PP_kInvalidFileHandle;
778 content::PepperPluginInstance* plugin_instance =
779 content::PepperPluginInstance::Get(instance);
780 if (!plugin_instance)
781 return PP_kInvalidFileHandle;
783 GURL gurl(file_url);
784 // Important security check. Do not remove.
785 if (!CanOpenViaFastPath(plugin_instance, gurl))
786 return PP_kInvalidFileHandle;
788 IPC::PlatformFileForTransit out_fd = IPC::InvalidPlatformFileForTransit();
789 IPC::Sender* sender = content::RenderThread::Get();
790 DCHECK(sender);
791 *nonce_lo = 0;
792 *nonce_hi = 0;
793 base::FilePath file_path;
794 if (!sender->Send(
795 new NaClHostMsg_OpenNaClExecutable(GetRoutingID(instance),
796 GURL(file_url),
797 !load_manager->nonsfi(),
798 &out_fd,
799 nonce_lo,
800 nonce_hi))) {
801 return PP_kInvalidFileHandle;
804 if (out_fd == IPC::InvalidPlatformFileForTransit())
805 return PP_kInvalidFileHandle;
807 return IPC::PlatformFileForTransitToPlatformFile(out_fd);
810 void DispatchEvent(PP_Instance instance,
811 PP_NaClEventType event_type,
812 const char* resource_url,
813 PP_Bool length_is_computable,
814 uint64_t loaded_bytes,
815 uint64_t total_bytes) {
816 ProgressEvent event(event_type,
817 resource_url,
818 PP_ToBool(length_is_computable),
819 loaded_bytes,
820 total_bytes);
821 DispatchProgressEvent(instance, event);
824 void ReportLoadError(PP_Instance instance,
825 PP_NaClError error,
826 const char* error_message) {
827 NexeLoadManager* load_manager = GetNexeLoadManager(instance);
828 if (load_manager)
829 load_manager->ReportLoadError(error, error_message);
832 void InstanceCreated(PP_Instance instance) {
833 InstanceMap& map = g_instance_map.Get();
834 CHECK(map.find(instance) == map.end()); // Sanity check.
835 scoped_ptr<NaClPluginInstance> new_instance(new NaClPluginInstance(instance));
836 map.add(instance, new_instance.Pass());
839 void InstanceDestroyed(PP_Instance instance) {
840 InstanceMap& map = g_instance_map.Get();
841 InstanceMap::iterator iter = map.find(instance);
842 CHECK(iter != map.end());
843 // The erase may call NexeLoadManager's destructor prior to removing it from
844 // the map. In that case, it is possible for the trusted Plugin to re-enter
845 // the NexeLoadManager (e.g., by calling ReportLoadError). Passing out the
846 // NexeLoadManager to a local scoped_ptr just ensures that its entry is gone
847 // from the map prior to the destructor being invoked.
848 scoped_ptr<NaClPluginInstance> temp(map.take(instance));
849 map.erase(iter);
852 PP_Bool NaClDebugEnabledForURL(const char* alleged_nmf_url) {
853 if (!base::CommandLine::ForCurrentProcess()->HasSwitch(
854 switches::kEnableNaClDebug))
855 return PP_FALSE;
856 IPC::Sender* sender = content::RenderThread::Get();
857 DCHECK(sender);
858 bool should_debug = false;
859 return PP_FromBool(
860 sender->Send(new NaClHostMsg_NaClDebugEnabledForURL(GURL(alleged_nmf_url),
861 &should_debug)) &&
862 should_debug);
865 void Vlog(const char* message) {
866 VLOG(1) << message;
869 void InitializePlugin(PP_Instance instance,
870 uint32_t argc,
871 const char* argn[],
872 const char* argv[]) {
873 NexeLoadManager* load_manager = GetNexeLoadManager(instance);
874 DCHECK(load_manager);
875 if (load_manager)
876 load_manager->InitializePlugin(argc, argn, argv);
879 void DownloadManifestToBuffer(PP_Instance instance,
880 struct PP_CompletionCallback callback);
882 bool CreateJsonManifest(PP_Instance instance,
883 const std::string& manifest_url,
884 const std::string& manifest_data);
886 void RequestNaClManifest(PP_Instance instance,
887 PP_CompletionCallback callback) {
888 NexeLoadManager* load_manager = GetNexeLoadManager(instance);
889 DCHECK(load_manager);
890 if (!load_manager) {
891 ppapi::PpapiGlobals::Get()->GetMainThreadMessageLoop()->PostTask(
892 FROM_HERE,
893 base::Bind(callback.func, callback.user_data,
894 static_cast<int32_t>(PP_ERROR_FAILED)));
895 return;
898 std::string url = load_manager->GetManifestURLArgument();
899 if (url.empty() || !load_manager->RequestNaClManifest(url)) {
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 const GURL& base_url = load_manager->manifest_base_url();
908 if (base_url.SchemeIs("data")) {
909 GURL gurl(base_url);
910 std::string mime_type;
911 std::string charset;
912 std::string data;
913 int32_t error = PP_ERROR_FAILED;
914 if (net::DataURL::Parse(gurl, &mime_type, &charset, &data)) {
915 if (data.size() <= ManifestDownloader::kNaClManifestMaxFileBytes) {
916 if (CreateJsonManifest(instance, base_url.spec(), data))
917 error = PP_OK;
918 } else {
919 load_manager->ReportLoadError(PP_NACL_ERROR_MANIFEST_TOO_LARGE,
920 "manifest file too large.");
922 } else {
923 load_manager->ReportLoadError(PP_NACL_ERROR_MANIFEST_LOAD_URL,
924 "could not load manifest url.");
926 ppapi::PpapiGlobals::Get()->GetMainThreadMessageLoop()->PostTask(
927 FROM_HERE,
928 base::Bind(callback.func, callback.user_data, error));
929 } else {
930 DownloadManifestToBuffer(instance, callback);
934 PP_Var GetManifestBaseURL(PP_Instance instance) {
935 NexeLoadManager* load_manager = GetNexeLoadManager(instance);
936 DCHECK(load_manager);
937 if (!load_manager)
938 return PP_MakeUndefined();
939 const GURL& gurl = load_manager->manifest_base_url();
940 if (!gurl.is_valid())
941 return PP_MakeUndefined();
942 return ppapi::StringVar::StringToPPVar(gurl.spec());
945 void ProcessNaClManifest(PP_Instance instance, const char* program_url) {
946 nacl::NexeLoadManager* load_manager = GetNexeLoadManager(instance);
947 if (load_manager)
948 load_manager->ProcessNaClManifest(program_url);
951 void DownloadManifestToBufferCompletion(PP_Instance instance,
952 struct PP_CompletionCallback callback,
953 base::Time start_time,
954 PP_NaClError pp_nacl_error,
955 const std::string& data);
957 void DownloadManifestToBuffer(PP_Instance instance,
958 struct PP_CompletionCallback callback) {
959 nacl::NexeLoadManager* load_manager = GetNexeLoadManager(instance);
960 DCHECK(load_manager);
961 content::PepperPluginInstance* plugin_instance =
962 content::PepperPluginInstance::Get(instance);
963 if (!load_manager || !plugin_instance) {
964 ppapi::PpapiGlobals::Get()->GetMainThreadMessageLoop()->PostTask(
965 FROM_HERE,
966 base::Bind(callback.func, callback.user_data,
967 static_cast<int32_t>(PP_ERROR_FAILED)));
969 const blink::WebDocument& document =
970 plugin_instance->GetContainer()->element().document();
972 const GURL& gurl = load_manager->manifest_base_url();
973 scoped_ptr<blink::WebURLLoader> url_loader(
974 CreateWebURLLoader(document, gurl));
975 blink::WebURLRequest request = CreateWebURLRequest(document, gurl);
977 // ManifestDownloader deletes itself after invoking the callback.
978 ManifestDownloader* manifest_downloader = new ManifestDownloader(
979 url_loader.Pass(),
980 load_manager->is_installed(),
981 base::Bind(DownloadManifestToBufferCompletion,
982 instance, callback, base::Time::Now()));
983 manifest_downloader->Load(request);
986 void DownloadManifestToBufferCompletion(PP_Instance instance,
987 struct PP_CompletionCallback callback,
988 base::Time start_time,
989 PP_NaClError pp_nacl_error,
990 const std::string& data) {
991 base::TimeDelta download_time = base::Time::Now() - start_time;
992 HistogramTimeSmall("NaCl.Perf.StartupTime.ManifestDownload",
993 download_time.InMilliseconds());
995 nacl::NexeLoadManager* load_manager = GetNexeLoadManager(instance);
996 if (!load_manager) {
997 callback.func(callback.user_data, PP_ERROR_ABORTED);
998 return;
1001 int32_t pp_error;
1002 switch (pp_nacl_error) {
1003 case PP_NACL_ERROR_LOAD_SUCCESS:
1004 pp_error = PP_OK;
1005 break;
1006 case PP_NACL_ERROR_MANIFEST_LOAD_URL:
1007 pp_error = PP_ERROR_FAILED;
1008 load_manager->ReportLoadError(PP_NACL_ERROR_MANIFEST_LOAD_URL,
1009 "could not load manifest url.");
1010 break;
1011 case PP_NACL_ERROR_MANIFEST_TOO_LARGE:
1012 pp_error = PP_ERROR_FILETOOBIG;
1013 load_manager->ReportLoadError(PP_NACL_ERROR_MANIFEST_TOO_LARGE,
1014 "manifest file too large.");
1015 break;
1016 case PP_NACL_ERROR_MANIFEST_NOACCESS_URL:
1017 pp_error = PP_ERROR_NOACCESS;
1018 load_manager->ReportLoadError(PP_NACL_ERROR_MANIFEST_NOACCESS_URL,
1019 "access to manifest url was denied.");
1020 break;
1021 default:
1022 NOTREACHED();
1023 pp_error = PP_ERROR_FAILED;
1024 load_manager->ReportLoadError(PP_NACL_ERROR_MANIFEST_LOAD_URL,
1025 "could not load manifest url.");
1028 if (pp_error == PP_OK) {
1029 std::string base_url = load_manager->manifest_base_url().spec();
1030 if (!CreateJsonManifest(instance, base_url, data))
1031 pp_error = PP_ERROR_FAILED;
1033 callback.func(callback.user_data, pp_error);
1036 bool CreateJsonManifest(PP_Instance instance,
1037 const std::string& manifest_url,
1038 const std::string& manifest_data) {
1039 HistogramSizeKB("NaCl.Perf.Size.Manifest",
1040 static_cast<int32_t>(manifest_data.length() / 1024));
1042 nacl::NexeLoadManager* load_manager = GetNexeLoadManager(instance);
1043 if (!load_manager)
1044 return false;
1046 const char* isa_type;
1047 if (load_manager->IsPNaCl())
1048 isa_type = kPortableArch;
1049 else
1050 isa_type = GetSandboxArch();
1052 scoped_ptr<nacl::JsonManifest> j(
1053 new nacl::JsonManifest(
1054 manifest_url.c_str(),
1055 isa_type,
1056 IsNonSFIModeEnabled(),
1057 PP_ToBool(NaClDebugEnabledForURL(manifest_url.c_str()))));
1058 JsonManifest::ErrorInfo error_info;
1059 if (j->Init(manifest_data.c_str(), &error_info)) {
1060 GetNaClPluginInstance(instance)->json_manifest.reset(j.release());
1061 return true;
1063 load_manager->ReportLoadError(error_info.error, error_info.string);
1064 return false;
1067 PP_Bool ManifestGetProgramURL(PP_Instance instance,
1068 PP_Var* pp_full_url,
1069 PP_PNaClOptions* pnacl_options,
1070 PP_Bool* pp_uses_nonsfi_mode) {
1071 nacl::NexeLoadManager* load_manager = GetNexeLoadManager(instance);
1073 JsonManifest* manifest = GetJsonManifest(instance);
1074 if (manifest == NULL)
1075 return PP_FALSE;
1077 bool uses_nonsfi_mode;
1078 std::string full_url;
1079 JsonManifest::ErrorInfo error_info;
1080 if (manifest->GetProgramURL(&full_url, pnacl_options, &uses_nonsfi_mode,
1081 &error_info)) {
1082 *pp_full_url = ppapi::StringVar::StringToPPVar(full_url);
1083 *pp_uses_nonsfi_mode = PP_FromBool(uses_nonsfi_mode);
1084 // Check if we should use Subzero (x86-32 / non-debugging case for now).
1085 if (pnacl_options->opt_level == 0 && !pnacl_options->is_debug &&
1086 strcmp(GetSandboxArch(), "x86-32") == 0 &&
1087 base::CommandLine::ForCurrentProcess()->HasSwitch(
1088 switches::kEnablePNaClSubzero)) {
1089 pnacl_options->use_subzero = PP_TRUE;
1090 // Subzero -O2 is closer to LLC -O0, so indicate -O2.
1091 pnacl_options->opt_level = 2;
1093 return PP_TRUE;
1096 if (load_manager)
1097 load_manager->ReportLoadError(error_info.error, error_info.string);
1098 return PP_FALSE;
1101 bool ManifestResolveKey(PP_Instance instance,
1102 bool is_helper_process,
1103 const std::string& key,
1104 std::string* full_url,
1105 PP_PNaClOptions* pnacl_options) {
1106 // For "helper" processes (llc and ld, for PNaCl translation), we resolve
1107 // keys manually as there is no existing .nmf file to parse.
1108 if (is_helper_process) {
1109 pnacl_options->translate = PP_FALSE;
1110 // We can only resolve keys in the files/ namespace.
1111 const std::string kFilesPrefix = "files/";
1112 if (key.find(kFilesPrefix) == std::string::npos) {
1113 nacl::NexeLoadManager* load_manager = GetNexeLoadManager(instance);
1114 if (load_manager)
1115 load_manager->ReportLoadError(PP_NACL_ERROR_MANIFEST_RESOLVE_URL,
1116 "key did not start with files/");
1117 return false;
1119 std::string key_basename = key.substr(kFilesPrefix.length());
1120 *full_url = std::string(kPNaClTranslatorBaseUrl) + GetSandboxArch() + "/" +
1121 key_basename;
1122 return true;
1125 JsonManifest* manifest = GetJsonManifest(instance);
1126 if (manifest == NULL)
1127 return false;
1129 return manifest->ResolveKey(key, full_url, pnacl_options);
1132 PP_Bool GetPNaClResourceInfo(PP_Instance instance,
1133 PP_Var* llc_tool_name,
1134 PP_Var* ld_tool_name,
1135 PP_Var* subzero_tool_name) {
1136 static const char kFilename[] = "chrome://pnacl-translator/pnacl.json";
1137 NexeLoadManager* load_manager = GetNexeLoadManager(instance);
1138 DCHECK(load_manager);
1139 if (!load_manager)
1140 return PP_FALSE;
1142 uint64_t nonce_lo = 0;
1143 uint64_t nonce_hi = 0;
1144 base::File file(GetReadonlyPnaclFd(kFilename, false /* is_executable */,
1145 &nonce_lo, &nonce_hi));
1146 if (!file.IsValid()) {
1147 load_manager->ReportLoadError(
1148 PP_NACL_ERROR_PNACL_RESOURCE_FETCH,
1149 "The Portable Native Client (pnacl) component is not "
1150 "installed. Please consult chrome://components for more "
1151 "information.");
1152 return PP_FALSE;
1155 base::File::Info file_info;
1156 if (!file.GetInfo(&file_info)) {
1157 load_manager->ReportLoadError(
1158 PP_NACL_ERROR_PNACL_RESOURCE_FETCH,
1159 std::string("GetPNaClResourceInfo, GetFileInfo failed for: ") +
1160 kFilename);
1161 return PP_FALSE;
1164 if (file_info.size > 1 << 20) {
1165 load_manager->ReportLoadError(
1166 PP_NACL_ERROR_PNACL_RESOURCE_FETCH,
1167 std::string("GetPNaClResourceInfo, file too large: ") + kFilename);
1168 return PP_FALSE;
1171 scoped_ptr<char[]> buffer(new char[file_info.size + 1]);
1172 if (buffer.get() == NULL) {
1173 load_manager->ReportLoadError(
1174 PP_NACL_ERROR_PNACL_RESOURCE_FETCH,
1175 std::string("GetPNaClResourceInfo, couldn't allocate for: ") +
1176 kFilename);
1177 return PP_FALSE;
1180 int rc = file.Read(0, buffer.get(), file_info.size);
1181 if (rc < 0) {
1182 load_manager->ReportLoadError(
1183 PP_NACL_ERROR_PNACL_RESOURCE_FETCH,
1184 std::string("GetPNaClResourceInfo, reading failed for: ") + kFilename);
1185 return PP_FALSE;
1188 // Null-terminate the bytes we we read from the file.
1189 buffer.get()[rc] = 0;
1191 // Expect the JSON file to contain a top-level object (dictionary).
1192 Json::Reader json_reader;
1193 Json::Value json_data;
1194 if (!json_reader.parse(buffer.get(), json_data)) {
1195 load_manager->ReportLoadError(
1196 PP_NACL_ERROR_PNACL_RESOURCE_FETCH,
1197 std::string("Parsing resource info failed: JSON parse error: ") +
1198 json_reader.getFormattedErrorMessages());
1199 return PP_FALSE;
1202 if (!json_data.isObject()) {
1203 load_manager->ReportLoadError(
1204 PP_NACL_ERROR_PNACL_RESOURCE_FETCH,
1205 "Parsing resource info failed: Malformed JSON dictionary");
1206 return PP_FALSE;
1209 if (json_data.isMember("pnacl-llc-name")) {
1210 Json::Value json_name = json_data["pnacl-llc-name"];
1211 if (json_name.isString()) {
1212 *llc_tool_name = ppapi::StringVar::StringToPPVar(json_name.asString());
1216 if (json_data.isMember("pnacl-ld-name")) {
1217 Json::Value json_name = json_data["pnacl-ld-name"];
1218 if (json_name.isString()) {
1219 *ld_tool_name = ppapi::StringVar::StringToPPVar(json_name.asString());
1223 if (json_data.isMember("pnacl-sz-name")) {
1224 Json::Value json_name = json_data["pnacl-sz-name"];
1225 if (json_name.isString()) {
1226 *subzero_tool_name =
1227 ppapi::StringVar::StringToPPVar(json_name.asString());
1229 } else {
1230 // TODO(jvoung): remove fallback after one chrome release
1231 // or when we bump the kMinPnaclVersion.
1232 // TODO(jvoung): Just use strings instead of PP_Var!
1233 *subzero_tool_name = ppapi::StringVar::StringToPPVar("pnacl-sz.nexe");
1236 return PP_TRUE;
1239 PP_Var GetCpuFeatureAttrs() {
1240 return ppapi::StringVar::StringToPPVar(GetCpuFeatures());
1243 // Encapsulates some of the state for a call to DownloadNexe to prevent
1244 // argument lists from getting too long.
1245 struct DownloadNexeRequest {
1246 PP_Instance instance;
1247 std::string url;
1248 PP_CompletionCallback callback;
1249 base::Time start_time;
1252 // A utility class to ensure that we don't send progress events more often than
1253 // every 10ms for a given file.
1254 class ProgressEventRateLimiter {
1255 public:
1256 explicit ProgressEventRateLimiter(PP_Instance instance)
1257 : instance_(instance) { }
1259 void ReportProgress(const std::string& url,
1260 int64_t total_bytes_received,
1261 int64_t total_bytes_to_be_received) {
1262 base::Time now = base::Time::Now();
1263 if (now - last_event_ > base::TimeDelta::FromMilliseconds(10)) {
1264 DispatchProgressEvent(instance_,
1265 ProgressEvent(PP_NACL_EVENT_PROGRESS,
1266 url,
1267 total_bytes_to_be_received >= 0,
1268 total_bytes_received,
1269 total_bytes_to_be_received));
1270 last_event_ = now;
1274 private:
1275 PP_Instance instance_;
1276 base::Time last_event_;
1279 void DownloadNexeCompletion(const DownloadNexeRequest& request,
1280 PP_NaClFileInfo* out_file_info,
1281 FileDownloader::Status status,
1282 base::File target_file,
1283 int http_status);
1285 void DownloadNexe(PP_Instance instance,
1286 const char* url,
1287 PP_NaClFileInfo* out_file_info,
1288 PP_CompletionCallback callback) {
1289 CHECK(url);
1290 CHECK(out_file_info);
1291 DownloadNexeRequest request;
1292 request.instance = instance;
1293 request.url = url;
1294 request.callback = callback;
1295 request.start_time = base::Time::Now();
1297 // Try the fast path for retrieving the file first.
1298 PP_FileHandle handle = OpenNaClExecutable(instance,
1299 url,
1300 &out_file_info->token_lo,
1301 &out_file_info->token_hi);
1302 if (handle != PP_kInvalidFileHandle) {
1303 DownloadNexeCompletion(request,
1304 out_file_info,
1305 FileDownloader::SUCCESS,
1306 base::File(handle),
1307 200);
1308 return;
1311 // The fast path didn't work, we'll fetch the file using URLLoader and write
1312 // it to local storage.
1313 base::File target_file(CreateTemporaryFile(instance));
1314 GURL gurl(url);
1316 content::PepperPluginInstance* plugin_instance =
1317 content::PepperPluginInstance::Get(instance);
1318 if (!plugin_instance) {
1319 ppapi::PpapiGlobals::Get()->GetMainThreadMessageLoop()->PostTask(
1320 FROM_HERE,
1321 base::Bind(callback.func, callback.user_data,
1322 static_cast<int32_t>(PP_ERROR_FAILED)));
1324 const blink::WebDocument& document =
1325 plugin_instance->GetContainer()->element().document();
1326 scoped_ptr<blink::WebURLLoader> url_loader(
1327 CreateWebURLLoader(document, gurl));
1328 blink::WebURLRequest url_request = CreateWebURLRequest(document, gurl);
1330 ProgressEventRateLimiter* tracker = new ProgressEventRateLimiter(instance);
1332 // FileDownloader deletes itself after invoking DownloadNexeCompletion.
1333 FileDownloader* file_downloader = new FileDownloader(
1334 url_loader.Pass(),
1335 target_file.Pass(),
1336 base::Bind(&DownloadNexeCompletion, request, out_file_info),
1337 base::Bind(&ProgressEventRateLimiter::ReportProgress,
1338 base::Owned(tracker), std::string(url)));
1339 file_downloader->Load(url_request);
1342 void DownloadNexeCompletion(const DownloadNexeRequest& request,
1343 PP_NaClFileInfo* out_file_info,
1344 FileDownloader::Status status,
1345 base::File target_file,
1346 int http_status) {
1347 int32_t pp_error = FileDownloaderToPepperError(status);
1348 int64_t bytes_read = -1;
1349 if (pp_error == PP_OK && target_file.IsValid()) {
1350 base::File::Info info;
1351 if (target_file.GetInfo(&info))
1352 bytes_read = info.size;
1355 if (bytes_read == -1) {
1356 target_file.Close();
1357 pp_error = PP_ERROR_FAILED;
1360 base::TimeDelta download_time = base::Time::Now() - request.start_time;
1362 NexeLoadManager* load_manager = GetNexeLoadManager(request.instance);
1363 if (load_manager) {
1364 load_manager->NexeFileDidOpen(pp_error,
1365 target_file,
1366 http_status,
1367 bytes_read,
1368 request.url,
1369 download_time);
1372 if (pp_error == PP_OK && target_file.IsValid())
1373 out_file_info->handle = target_file.TakePlatformFile();
1374 else
1375 out_file_info->handle = PP_kInvalidFileHandle;
1377 request.callback.func(request.callback.user_data, pp_error);
1380 void DownloadFileCompletion(
1381 const DownloadFileCallback& callback,
1382 FileDownloader::Status status,
1383 base::File file,
1384 int http_status) {
1385 int32_t pp_error = FileDownloaderToPepperError(status);
1386 PP_NaClFileInfo file_info;
1387 if (pp_error == PP_OK) {
1388 file_info.handle = file.TakePlatformFile();
1389 file_info.token_lo = 0;
1390 file_info.token_hi = 0;
1391 } else {
1392 file_info = kInvalidNaClFileInfo;
1395 callback.Run(pp_error, file_info);
1398 void DownloadFile(PP_Instance instance,
1399 const std::string& url,
1400 const DownloadFileCallback& callback) {
1401 DCHECK(ppapi::PpapiGlobals::Get()->GetMainThreadMessageLoop()->
1402 BelongsToCurrentThread());
1404 NexeLoadManager* load_manager = GetNexeLoadManager(instance);
1405 DCHECK(load_manager);
1406 if (!load_manager) {
1407 base::MessageLoop::current()->PostTask(
1408 FROM_HERE,
1409 base::Bind(callback,
1410 static_cast<int32_t>(PP_ERROR_FAILED),
1411 kInvalidNaClFileInfo));
1412 return;
1415 // Handle special PNaCl support files which are installed on the user's
1416 // machine.
1417 if (url.find(kPNaClTranslatorBaseUrl, 0) == 0) {
1418 PP_NaClFileInfo file_info = kInvalidNaClFileInfo;
1419 PP_FileHandle handle = GetReadonlyPnaclFd(url.c_str(),
1420 false /* is_executable */,
1421 &file_info.token_lo,
1422 &file_info.token_hi);
1423 if (handle == PP_kInvalidFileHandle) {
1424 base::MessageLoop::current()->PostTask(
1425 FROM_HERE,
1426 base::Bind(callback,
1427 static_cast<int32_t>(PP_ERROR_FAILED),
1428 kInvalidNaClFileInfo));
1429 return;
1431 file_info.handle = handle;
1432 base::MessageLoop::current()->PostTask(
1433 FROM_HERE,
1434 base::Bind(callback, static_cast<int32_t>(PP_OK), file_info));
1435 return;
1438 // We have to ensure that this url resolves relative to the plugin base url
1439 // before downloading it.
1440 const GURL& test_gurl = load_manager->plugin_base_url().Resolve(url);
1441 if (!test_gurl.is_valid()) {
1442 base::MessageLoop::current()->PostTask(
1443 FROM_HERE,
1444 base::Bind(callback,
1445 static_cast<int32_t>(PP_ERROR_FAILED),
1446 kInvalidNaClFileInfo));
1447 return;
1450 // Try the fast path for retrieving the file first.
1451 uint64_t file_token_lo = 0;
1452 uint64_t file_token_hi = 0;
1453 PP_FileHandle file_handle = OpenNaClExecutable(instance,
1454 url.c_str(),
1455 &file_token_lo,
1456 &file_token_hi);
1457 if (file_handle != PP_kInvalidFileHandle) {
1458 PP_NaClFileInfo file_info;
1459 file_info.handle = file_handle;
1460 file_info.token_lo = file_token_lo;
1461 file_info.token_hi = file_token_hi;
1462 base::MessageLoop::current()->PostTask(
1463 FROM_HERE,
1464 base::Bind(callback, static_cast<int32_t>(PP_OK), file_info));
1465 return;
1468 // The fast path didn't work, we'll fetch the file using URLLoader and write
1469 // it to local storage.
1470 base::File target_file(CreateTemporaryFile(instance));
1471 GURL gurl(url);
1473 content::PepperPluginInstance* plugin_instance =
1474 content::PepperPluginInstance::Get(instance);
1475 if (!plugin_instance) {
1476 base::MessageLoop::current()->PostTask(
1477 FROM_HERE,
1478 base::Bind(callback,
1479 static_cast<int32_t>(PP_ERROR_FAILED),
1480 kInvalidNaClFileInfo));
1482 const blink::WebDocument& document =
1483 plugin_instance->GetContainer()->element().document();
1484 scoped_ptr<blink::WebURLLoader> url_loader(
1485 CreateWebURLLoader(document, gurl));
1486 blink::WebURLRequest url_request = CreateWebURLRequest(document, gurl);
1488 ProgressEventRateLimiter* tracker = new ProgressEventRateLimiter(instance);
1490 // FileDownloader deletes itself after invoking DownloadNexeCompletion.
1491 FileDownloader* file_downloader = new FileDownloader(
1492 url_loader.Pass(),
1493 target_file.Pass(),
1494 base::Bind(&DownloadFileCompletion, callback),
1495 base::Bind(&ProgressEventRateLimiter::ReportProgress,
1496 base::Owned(tracker), std::string(url)));
1497 file_downloader->Load(url_request);
1500 void ReportSelLdrStatus(PP_Instance instance,
1501 int32_t load_status,
1502 int32_t max_status) {
1503 HistogramEnumerate("NaCl.LoadStatus.SelLdr", load_status, max_status);
1504 NexeLoadManager* load_manager = GetNexeLoadManager(instance);
1505 DCHECK(load_manager);
1506 if (!load_manager)
1507 return;
1509 // Gather data to see if being installed changes load outcomes.
1510 const char* name = load_manager->is_installed() ?
1511 "NaCl.LoadStatus.SelLdr.InstalledApp" :
1512 "NaCl.LoadStatus.SelLdr.NotInstalledApp";
1513 HistogramEnumerate(name, load_status, max_status);
1516 void LogTranslateTime(const char* histogram_name,
1517 int64_t time_in_us) {
1518 ppapi::PpapiGlobals::Get()->GetMainThreadMessageLoop()->PostTask(
1519 FROM_HERE,
1520 base::Bind(&HistogramTimeTranslation,
1521 std::string(histogram_name),
1522 time_in_us / 1000));
1525 void LogBytesCompiledVsDowloaded(PP_Bool use_subzero,
1526 int64_t pexe_bytes_compiled,
1527 int64_t pexe_bytes_downloaded) {
1528 HistogramRatio("NaCl.Perf.PNaClLoadTime.PctCompiledWhenFullyDownloaded",
1529 pexe_bytes_compiled, pexe_bytes_downloaded);
1530 HistogramRatio(
1531 use_subzero
1532 ? "NaCl.Perf.PNaClLoadTime.PctCompiledWhenFullyDownloaded.Subzero"
1533 : "NaCl.Perf.PNaClLoadTime.PctCompiledWhenFullyDownloaded.LLC",
1534 pexe_bytes_compiled, pexe_bytes_downloaded);
1537 void SetPNaClStartTime(PP_Instance instance) {
1538 NexeLoadManager* load_manager = GetNexeLoadManager(instance);
1539 if (load_manager)
1540 load_manager->set_pnacl_start_time(base::Time::Now());
1543 // PexeDownloader is responsible for deleting itself when the download
1544 // finishes.
1545 class PexeDownloader : public blink::WebURLLoaderClient {
1546 public:
1547 PexeDownloader(PP_Instance instance,
1548 scoped_ptr<blink::WebURLLoader> url_loader,
1549 const std::string& pexe_url,
1550 int32_t pexe_opt_level,
1551 bool use_subzero,
1552 const PPP_PexeStreamHandler* stream_handler,
1553 void* stream_handler_user_data)
1554 : instance_(instance),
1555 url_loader_(url_loader.Pass()),
1556 pexe_url_(pexe_url),
1557 pexe_opt_level_(pexe_opt_level),
1558 use_subzero_(use_subzero),
1559 stream_handler_(stream_handler),
1560 stream_handler_user_data_(stream_handler_user_data),
1561 success_(false),
1562 expected_content_length_(-1),
1563 weak_factory_(this) {}
1565 void Load(const blink::WebURLRequest& request) {
1566 url_loader_->loadAsynchronously(request, this);
1569 private:
1570 virtual void didReceiveResponse(blink::WebURLLoader* loader,
1571 const blink::WebURLResponse& response) {
1572 success_ = (response.httpStatusCode() == 200);
1573 if (!success_)
1574 return;
1576 expected_content_length_ = response.expectedContentLength();
1578 // Defer loading after receiving headers. This is because we may already
1579 // have a cached translated nexe, so check for that now.
1580 url_loader_->setDefersLoading(true);
1582 std::string etag = response.httpHeaderField("etag").utf8();
1583 std::string last_modified =
1584 response.httpHeaderField("last-modified").utf8();
1585 base::Time last_modified_time;
1586 base::Time::FromString(last_modified.c_str(), &last_modified_time);
1588 bool has_no_store_header = false;
1589 std::string cache_control =
1590 response.httpHeaderField("cache-control").utf8();
1592 std::vector<std::string> values;
1593 base::SplitString(cache_control, ',', &values);
1594 for (std::vector<std::string>::const_iterator it = values.begin();
1595 it != values.end();
1596 ++it) {
1597 if (base::StringToLowerASCII(*it) == "no-store")
1598 has_no_store_header = true;
1601 GetNexeFd(
1602 instance_, pexe_url_, pexe_opt_level_, last_modified_time, etag,
1603 has_no_store_header, use_subzero_,
1604 base::Bind(&PexeDownloader::didGetNexeFd, weak_factory_.GetWeakPtr()));
1607 virtual void didGetNexeFd(int32_t pp_error,
1608 bool cache_hit,
1609 PP_FileHandle file_handle) {
1610 if (!content::PepperPluginInstance::Get(instance_)) {
1611 delete this;
1612 return;
1615 HistogramEnumerate("NaCl.Perf.PNaClCache.IsHit", cache_hit, 2);
1616 HistogramEnumerate(use_subzero_ ? "NaCl.Perf.PNaClCache.IsHit.Subzero"
1617 : "NaCl.Perf.PNaClCache.IsHit.LLC",
1618 cache_hit, 2);
1619 if (cache_hit) {
1620 stream_handler_->DidCacheHit(stream_handler_user_data_, file_handle);
1622 // We delete the PexeDownloader at this point since we successfully got a
1623 // cached, translated nexe.
1624 delete this;
1625 return;
1627 stream_handler_->DidCacheMiss(stream_handler_user_data_,
1628 expected_content_length_,
1629 file_handle);
1631 // No translated nexe was found in the cache, so we should download the
1632 // file to start streaming it.
1633 url_loader_->setDefersLoading(false);
1636 virtual void didReceiveData(blink::WebURLLoader* loader,
1637 const char* data,
1638 int data_length,
1639 int encoded_data_length) {
1640 if (content::PepperPluginInstance::Get(instance_)) {
1641 // Stream the data we received to the stream callback.
1642 stream_handler_->DidStreamData(stream_handler_user_data_,
1643 data,
1644 data_length);
1648 virtual void didFinishLoading(blink::WebURLLoader* loader,
1649 double finish_time,
1650 int64_t total_encoded_data_length) {
1651 int32_t result = success_ ? PP_OK : PP_ERROR_FAILED;
1653 if (content::PepperPluginInstance::Get(instance_))
1654 stream_handler_->DidFinishStream(stream_handler_user_data_, result);
1655 delete this;
1658 virtual void didFail(blink::WebURLLoader* loader,
1659 const blink::WebURLError& error) {
1660 success_ = false;
1663 PP_Instance instance_;
1664 scoped_ptr<blink::WebURLLoader> url_loader_;
1665 std::string pexe_url_;
1666 int32_t pexe_opt_level_;
1667 bool use_subzero_;
1668 const PPP_PexeStreamHandler* stream_handler_;
1669 void* stream_handler_user_data_;
1670 bool success_;
1671 int64_t expected_content_length_;
1672 base::WeakPtrFactory<PexeDownloader> weak_factory_;
1675 void StreamPexe(PP_Instance instance,
1676 const char* pexe_url,
1677 int32_t opt_level,
1678 PP_Bool use_subzero,
1679 const PPP_PexeStreamHandler* handler,
1680 void* handler_user_data) {
1681 content::PepperPluginInstance* plugin_instance =
1682 content::PepperPluginInstance::Get(instance);
1683 if (!plugin_instance) {
1684 base::MessageLoop::current()->PostTask(
1685 FROM_HERE,
1686 base::Bind(handler->DidFinishStream,
1687 handler_user_data,
1688 static_cast<int32_t>(PP_ERROR_FAILED)));
1689 return;
1692 GURL gurl(pexe_url);
1693 const blink::WebDocument& document =
1694 plugin_instance->GetContainer()->element().document();
1695 scoped_ptr<blink::WebURLLoader> url_loader(
1696 CreateWebURLLoader(document, gurl));
1697 PexeDownloader* downloader =
1698 new PexeDownloader(instance, url_loader.Pass(), pexe_url, opt_level,
1699 PP_ToBool(use_subzero), handler, handler_user_data);
1701 blink::WebURLRequest url_request = CreateWebURLRequest(document, gurl);
1702 // Mark the request as requesting a PNaCl bitcode file,
1703 // so that component updater can detect this user action.
1704 url_request.addHTTPHeaderField(
1705 blink::WebString::fromUTF8("Accept"),
1706 blink::WebString::fromUTF8("application/x-pnacl, */*"));
1707 url_request.setRequestContext(blink::WebURLRequest::RequestContextObject);
1708 downloader->Load(url_request);
1711 const PPB_NaCl_Private nacl_interface = {
1712 &LaunchSelLdr,
1713 &UrandomFD,
1714 &BrokerDuplicateHandle,
1715 &GetReadExecPnaclFd,
1716 &CreateTemporaryFile,
1717 &GetNumberOfProcessors,
1718 &ReportTranslationFinished,
1719 &DispatchEvent,
1720 &ReportLoadError,
1721 &InstanceCreated,
1722 &InstanceDestroyed,
1723 &GetSandboxArch,
1724 &Vlog,
1725 &InitializePlugin,
1726 &RequestNaClManifest,
1727 &GetManifestBaseURL,
1728 &ProcessNaClManifest,
1729 &ManifestGetProgramURL,
1730 &GetPNaClResourceInfo,
1731 &GetCpuFeatureAttrs,
1732 &DownloadNexe,
1733 &ReportSelLdrStatus,
1734 &LogTranslateTime,
1735 &LogBytesCompiledVsDowloaded,
1736 &SetPNaClStartTime,
1737 &StreamPexe
1740 } // namespace
1742 const PPB_NaCl_Private* GetNaClPrivateInterface() {
1743 return &nacl_interface;
1746 } // namespace nacl