ozone: evdev: Sync caps lock LED state to evdev
[chromium-blink-merge.git] / components / nacl / renderer / ppb_nacl_private_impl.cc
blob3c84afe5d1ee4a16187fef0b715342f5b239acf4
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 // This contains state that is produced by LaunchSelLdr() and consumed
93 // by StartPpapiProxy().
94 struct InstanceInfo {
95 InstanceInfo() : plugin_pid(base::kNullProcessId), plugin_child_id(0) {}
96 GURL url;
97 ppapi::PpapiPermissions permissions;
98 base::ProcessId plugin_pid;
99 int plugin_child_id;
100 IPC::ChannelHandle channel_handle;
103 class NaClPluginInstance {
104 public:
105 NaClPluginInstance(PP_Instance instance):
106 nexe_load_manager(instance), pexe_size(0) {}
108 NexeLoadManager nexe_load_manager;
109 scoped_ptr<JsonManifest> json_manifest;
110 scoped_ptr<InstanceInfo> instance_info;
112 // When translation is complete, this records the size of the pexe in
113 // bytes so that it can be reported in a later load event.
114 uint64_t pexe_size;
117 typedef base::ScopedPtrHashMap<PP_Instance, NaClPluginInstance> InstanceMap;
118 base::LazyInstance<InstanceMap> g_instance_map = LAZY_INSTANCE_INITIALIZER;
120 NaClPluginInstance* GetNaClPluginInstance(PP_Instance instance) {
121 InstanceMap& map = g_instance_map.Get();
122 InstanceMap::iterator iter = map.find(instance);
123 if (iter == map.end())
124 return NULL;
125 return iter->second;
128 NexeLoadManager* GetNexeLoadManager(PP_Instance instance) {
129 NaClPluginInstance* nacl_plugin_instance = GetNaClPluginInstance(instance);
130 if (!nacl_plugin_instance)
131 return NULL;
132 return &nacl_plugin_instance->nexe_load_manager;
135 JsonManifest* GetJsonManifest(PP_Instance instance) {
136 NaClPluginInstance* nacl_plugin_instance = GetNaClPluginInstance(instance);
137 if (!nacl_plugin_instance)
138 return NULL;
139 return nacl_plugin_instance->json_manifest.get();
142 static const PP_NaClFileInfo kInvalidNaClFileInfo = {
143 PP_kInvalidFileHandle,
144 0, // token_lo
145 0, // token_hi
148 int GetRoutingID(PP_Instance instance) {
149 // Check that we are on the main renderer thread.
150 DCHECK(content::RenderThread::Get());
151 content::RendererPpapiHost* host =
152 content::RendererPpapiHost::GetForPPInstance(instance);
153 if (!host)
154 return 0;
155 return host->GetRoutingIDForWidget(instance);
158 // Returns whether the channel_handle is valid or not.
159 bool IsValidChannelHandle(const IPC::ChannelHandle& channel_handle) {
160 if (channel_handle.name.empty()) {
161 return false;
164 #if defined(OS_POSIX)
165 if (channel_handle.socket.fd == -1) {
166 return false;
168 #endif
170 return true;
173 void PostPPCompletionCallback(PP_CompletionCallback callback,
174 int32_t status) {
175 ppapi::PpapiGlobals::Get()->GetMainThreadMessageLoop()->PostTask(
176 FROM_HERE,
177 base::Bind(callback.func, callback.user_data, status));
180 bool ManifestResolveKey(PP_Instance instance,
181 bool is_helper_process,
182 const std::string& key,
183 std::string* full_url,
184 PP_PNaClOptions* pnacl_options);
186 typedef base::Callback<void(int32_t, const PP_NaClFileInfo&)>
187 DownloadFileCallback;
189 void DownloadFile(PP_Instance instance,
190 const std::string& url,
191 const DownloadFileCallback& callback);
193 PP_Bool StartPpapiProxy(PP_Instance instance);
195 // Thin adapter from PPP_ManifestService to ManifestServiceChannel::Delegate.
196 // Note that user_data is managed by the caller of LaunchSelLdr. Please see
197 // also PP_ManifestService's comment for more details about resource
198 // management.
199 class ManifestServiceProxy : public ManifestServiceChannel::Delegate {
200 public:
201 ManifestServiceProxy(PP_Instance pp_instance, NaClAppProcessType process_type)
202 : pp_instance_(pp_instance), process_type_(process_type) {}
204 ~ManifestServiceProxy() override {}
206 void StartupInitializationComplete() override {
207 if (StartPpapiProxy(pp_instance_) == PP_TRUE) {
208 NaClPluginInstance* nacl_plugin_instance =
209 GetNaClPluginInstance(pp_instance_);
210 JsonManifest* manifest = GetJsonManifest(pp_instance_);
211 if (nacl_plugin_instance && manifest) {
212 NexeLoadManager* load_manager =
213 &nacl_plugin_instance->nexe_load_manager;
214 std::string full_url;
215 PP_PNaClOptions pnacl_options;
216 bool uses_nonsfi_mode;
217 JsonManifest::ErrorInfo error_info;
218 if (manifest->GetProgramURL(&full_url,
219 &pnacl_options,
220 &uses_nonsfi_mode,
221 &error_info)) {
222 int64_t exe_size = nacl_plugin_instance->pexe_size;
223 if (exe_size == 0)
224 exe_size = load_manager->nexe_size();
225 load_manager->ReportLoadSuccess(full_url, exe_size, exe_size);
231 void OpenResource(
232 const std::string& key,
233 const ManifestServiceChannel::OpenResourceCallback& callback) override {
234 DCHECK(ppapi::PpapiGlobals::Get()->GetMainThreadMessageLoop()->
235 BelongsToCurrentThread());
237 // For security hardening, disable open_resource() when it is isn't
238 // needed. PNaCl pexes can't use open_resource(), but general nexes
239 // and the PNaCl translator nexes may use it.
240 if (process_type_ != kNativeNaClProcessType &&
241 process_type_ != kPNaClTranslatorProcessType) {
242 // Return an error.
243 base::MessageLoop::current()->PostTask(
244 FROM_HERE,
245 base::Bind(callback, base::Passed(base::File()), 0, 0));
246 return;
249 std::string url;
250 // TODO(teravest): Clean up pnacl_options logic in JsonManifest so we don't
251 // have to initialize it like this here.
252 PP_PNaClOptions pnacl_options;
253 pnacl_options.translate = PP_FALSE;
254 pnacl_options.is_debug = PP_FALSE;
255 pnacl_options.opt_level = 2;
256 bool is_helper_process = process_type_ == kPNaClTranslatorProcessType;
257 if (!ManifestResolveKey(pp_instance_, is_helper_process, key, &url,
258 &pnacl_options)) {
259 base::MessageLoop::current()->PostTask(
260 FROM_HERE,
261 base::Bind(callback, base::Passed(base::File()), 0, 0));
262 return;
265 // We have to call DidDownloadFile, even if this object is destroyed, so
266 // that the handle inside PP_NaClFileInfo isn't leaked. This means that the
267 // callback passed to this function shouldn't have a weak pointer to an
268 // object either.
270 // TODO(teravest): Make a type like PP_NaClFileInfo to use for DownloadFile
271 // that would close the file handle on destruction.
272 DownloadFile(pp_instance_, url,
273 base::Bind(&ManifestServiceProxy::DidDownloadFile, callback));
276 private:
277 static void DidDownloadFile(
278 ManifestServiceChannel::OpenResourceCallback callback,
279 int32_t pp_error,
280 const PP_NaClFileInfo& file_info) {
281 if (pp_error != PP_OK) {
282 callback.Run(base::File(), 0, 0);
283 return;
285 callback.Run(base::File(file_info.handle),
286 file_info.token_lo,
287 file_info.token_hi);
290 PP_Instance pp_instance_;
291 NaClAppProcessType process_type_;
292 DISALLOW_COPY_AND_ASSIGN(ManifestServiceProxy);
295 blink::WebURLLoader* CreateWebURLLoader(const blink::WebDocument& document,
296 const GURL& gurl) {
297 blink::WebURLLoaderOptions options;
298 options.untrustedHTTP = true;
300 // Options settings here follow the original behavior in the trusted
301 // plugin and PepperURLLoaderHost.
302 if (document.securityOrigin().canRequest(gurl)) {
303 options.allowCredentials = true;
304 } else {
305 // Allow CORS.
306 options.crossOriginRequestPolicy =
307 blink::WebURLLoaderOptions::CrossOriginRequestPolicyUseAccessControl;
309 return document.frame()->createAssociatedURLLoader(options);
312 blink::WebURLRequest CreateWebURLRequest(const blink::WebDocument& document,
313 const GURL& gurl) {
314 blink::WebURLRequest request;
315 request.initialize();
316 request.setURL(gurl);
317 request.setFirstPartyForCookies(document.firstPartyForCookies());
318 return request;
321 int32_t FileDownloaderToPepperError(FileDownloader::Status status) {
322 switch (status) {
323 case FileDownloader::SUCCESS:
324 return PP_OK;
325 case FileDownloader::ACCESS_DENIED:
326 return PP_ERROR_NOACCESS;
327 case FileDownloader::FAILED:
328 return PP_ERROR_FAILED;
329 // No default case, to catch unhandled Status values.
331 return PP_ERROR_FAILED;
334 NaClAppProcessType PP_ToNaClAppProcessType(
335 PP_NaClAppProcessType pp_process_type) {
336 #define STATICALLY_CHECK_NACLAPPPROCESSTYPE_EQ(pp, nonpp) \
337 static_assert(static_cast<int>(pp) == static_cast<int>(nonpp), \
338 "PP_NaClAppProcessType differs from NaClAppProcessType");
339 STATICALLY_CHECK_NACLAPPPROCESSTYPE_EQ(PP_UNKNOWN_NACL_PROCESS_TYPE,
340 kUnknownNaClProcessType);
341 STATICALLY_CHECK_NACLAPPPROCESSTYPE_EQ(PP_NATIVE_NACL_PROCESS_TYPE,
342 kNativeNaClProcessType);
343 STATICALLY_CHECK_NACLAPPPROCESSTYPE_EQ(PP_PNACL_PROCESS_TYPE,
344 kPNaClProcessType);
345 STATICALLY_CHECK_NACLAPPPROCESSTYPE_EQ(PP_PNACL_TRANSLATOR_PROCESS_TYPE,
346 kPNaClTranslatorProcessType);
347 STATICALLY_CHECK_NACLAPPPROCESSTYPE_EQ(PP_NUM_NACL_PROCESS_TYPES,
348 kNumNaClProcessTypes);
349 #undef STATICALLY_CHECK_NACLAPPPROCESSTYPE_EQ
350 DCHECK(pp_process_type > PP_UNKNOWN_NACL_PROCESS_TYPE &&
351 pp_process_type < PP_NUM_NACL_PROCESS_TYPES);
352 return static_cast<NaClAppProcessType>(pp_process_type);
355 // Launch NaCl's sel_ldr process.
356 void LaunchSelLdr(PP_Instance instance,
357 PP_Bool main_service_runtime,
358 const char* alleged_url,
359 const PP_NaClFileInfo* nexe_file_info,
360 PP_Bool uses_nonsfi_mode,
361 PP_NaClAppProcessType pp_process_type,
362 void* imc_handle,
363 PP_CompletionCallback callback) {
364 CHECK(ppapi::PpapiGlobals::Get()->GetMainThreadMessageLoop()->
365 BelongsToCurrentThread());
366 NaClAppProcessType process_type = PP_ToNaClAppProcessType(pp_process_type);
367 // Create the manifest service proxy here, so on error case, it will be
368 // destructed (without passing it to ManifestServiceChannel).
369 scoped_ptr<ManifestServiceChannel::Delegate> manifest_service_proxy(
370 new ManifestServiceProxy(instance, process_type));
372 FileDescriptor result_socket;
373 IPC::Sender* sender = content::RenderThread::Get();
374 DCHECK(sender);
375 int routing_id = GetRoutingID(instance);
376 NexeLoadManager* load_manager = GetNexeLoadManager(instance);
377 DCHECK(load_manager);
378 if (!routing_id || !load_manager) {
379 if (nexe_file_info->handle != PP_kInvalidFileHandle) {
380 base::File closer(nexe_file_info->handle);
382 ppapi::PpapiGlobals::Get()->GetMainThreadMessageLoop()->PostTask(
383 FROM_HERE, base::Bind(callback.func, callback.user_data,
384 static_cast<int32_t>(PP_ERROR_FAILED)));
385 return;
388 InstanceInfo instance_info;
389 instance_info.url = GURL(alleged_url);
391 uint32_t perm_bits = ppapi::PERMISSION_NONE;
392 // Conditionally block 'Dev' interfaces. We do this for the NaCl process, so
393 // it's clearer to developers when they are using 'Dev' inappropriately. We
394 // must also check on the trusted side of the proxy.
395 if (load_manager->DevInterfacesEnabled())
396 perm_bits |= ppapi::PERMISSION_DEV;
397 instance_info.permissions =
398 ppapi::PpapiPermissions::GetForCommandLine(perm_bits);
399 std::string error_message_string;
400 NaClLaunchResult launch_result;
402 IPC::PlatformFileForTransit nexe_for_transit =
403 IPC::InvalidPlatformFileForTransit();
404 #if defined(OS_POSIX)
405 if (nexe_file_info->handle != PP_kInvalidFileHandle)
406 nexe_for_transit = base::FileDescriptor(nexe_file_info->handle, true);
407 #elif defined(OS_WIN)
408 // Duplicate the handle on the browser side instead of the renderer.
409 // This is because BrokerGetFileForProcess isn't part of content/public, and
410 // it's simpler to do the duplication in the browser anyway.
411 nexe_for_transit = nexe_file_info->handle;
412 #else
413 #error Unsupported target platform.
414 #endif
415 if (!sender->Send(new NaClHostMsg_LaunchNaCl(
416 NaClLaunchParams(
417 instance_info.url.spec(),
418 nexe_for_transit,
419 nexe_file_info->token_lo,
420 nexe_file_info->token_hi,
421 routing_id,
422 perm_bits,
423 PP_ToBool(uses_nonsfi_mode),
424 process_type),
425 &launch_result,
426 &error_message_string))) {
427 ppapi::PpapiGlobals::Get()->GetMainThreadMessageLoop()->PostTask(
428 FROM_HERE,
429 base::Bind(callback.func, callback.user_data,
430 static_cast<int32_t>(PP_ERROR_FAILED)));
431 return;
434 load_manager->set_nonsfi(PP_ToBool(uses_nonsfi_mode));
436 if (!error_message_string.empty()) {
437 if (PP_ToBool(main_service_runtime)) {
438 load_manager->ReportLoadError(PP_NACL_ERROR_SEL_LDR_LAUNCH,
439 "ServiceRuntime: failed to start",
440 error_message_string);
442 ppapi::PpapiGlobals::Get()->GetMainThreadMessageLoop()->PostTask(
443 FROM_HERE,
444 base::Bind(callback.func, callback.user_data,
445 static_cast<int32_t>(PP_ERROR_FAILED)));
446 return;
448 result_socket = launch_result.imc_channel_handle;
449 instance_info.channel_handle = launch_result.ppapi_ipc_channel_handle;
450 instance_info.plugin_pid = launch_result.plugin_pid;
451 instance_info.plugin_child_id = launch_result.plugin_child_id;
453 // Don't save instance_info if channel handle is invalid.
454 if (IsValidChannelHandle(instance_info.channel_handle)) {
455 NaClPluginInstance* nacl_plugin_instance = GetNaClPluginInstance(instance);
456 nacl_plugin_instance->instance_info.reset(new InstanceInfo(instance_info));
459 *(static_cast<NaClHandle*>(imc_handle)) = ToNativeHandle(result_socket);
461 // Store the crash information shared memory handle.
462 load_manager->set_crash_info_shmem_handle(
463 launch_result.crash_info_shmem_handle);
465 // Create the trusted plugin channel.
466 if (IsValidChannelHandle(launch_result.trusted_ipc_channel_handle)) {
467 bool report_exit_status = PP_ToBool(main_service_runtime);
468 scoped_ptr<TrustedPluginChannel> trusted_plugin_channel(
469 new TrustedPluginChannel(
470 load_manager,
471 launch_result.trusted_ipc_channel_handle,
472 content::RenderThread::Get()->GetShutdownEvent(),
473 report_exit_status));
474 load_manager->set_trusted_plugin_channel(trusted_plugin_channel.Pass());
475 } else {
476 PostPPCompletionCallback(callback, PP_ERROR_FAILED);
477 return;
480 // Create the manifest service handle as well.
481 if (IsValidChannelHandle(launch_result.manifest_service_ipc_channel_handle)) {
482 scoped_ptr<ManifestServiceChannel> manifest_service_channel(
483 new ManifestServiceChannel(
484 launch_result.manifest_service_ipc_channel_handle,
485 base::Bind(&PostPPCompletionCallback, callback),
486 manifest_service_proxy.Pass(),
487 content::RenderThread::Get()->GetShutdownEvent()));
488 load_manager->set_manifest_service_channel(
489 manifest_service_channel.Pass());
493 PP_Bool StartPpapiProxy(PP_Instance instance) {
494 NexeLoadManager* load_manager = GetNexeLoadManager(instance);
495 DCHECK(load_manager);
496 if (!load_manager)
497 return PP_FALSE;
499 content::PepperPluginInstance* plugin_instance =
500 content::PepperPluginInstance::Get(instance);
501 if (!plugin_instance) {
502 DLOG(ERROR) << "GetInstance() failed";
503 return PP_FALSE;
506 NaClPluginInstance* nacl_plugin_instance = GetNaClPluginInstance(instance);
507 if (!nacl_plugin_instance->instance_info) {
508 DLOG(ERROR) << "Could not find instance ID";
509 return PP_FALSE;
511 scoped_ptr<InstanceInfo> instance_info =
512 nacl_plugin_instance->instance_info.Pass();
514 PP_ExternalPluginResult result = plugin_instance->SwitchToOutOfProcessProxy(
515 base::FilePath().AppendASCII(instance_info->url.spec()),
516 instance_info->permissions,
517 instance_info->channel_handle,
518 instance_info->plugin_pid,
519 instance_info->plugin_child_id);
521 if (result == PP_EXTERNAL_PLUGIN_OK) {
522 // Log the amound of time that has passed between the trusted plugin being
523 // initialized and the untrusted plugin being initialized. This is
524 // (roughly) the cost of using NaCl, in terms of startup time.
525 load_manager->ReportStartupOverhead();
526 return PP_TRUE;
527 } else if (result == PP_EXTERNAL_PLUGIN_ERROR_MODULE) {
528 load_manager->ReportLoadError(PP_NACL_ERROR_START_PROXY_MODULE,
529 "could not initialize module.");
530 } else if (result == PP_EXTERNAL_PLUGIN_ERROR_INSTANCE) {
531 load_manager->ReportLoadError(PP_NACL_ERROR_START_PROXY_MODULE,
532 "could not create instance.");
534 return PP_FALSE;
537 int UrandomFD(void) {
538 #if defined(OS_POSIX)
539 return base::GetUrandomFD();
540 #else
541 return -1;
542 #endif
545 int32_t BrokerDuplicateHandle(PP_FileHandle source_handle,
546 uint32_t process_id,
547 PP_FileHandle* target_handle,
548 uint32_t desired_access,
549 uint32_t options) {
550 #if defined(OS_WIN)
551 return content::BrokerDuplicateHandle(source_handle, process_id,
552 target_handle, desired_access,
553 options);
554 #else
555 return 0;
556 #endif
559 // Convert a URL to a filename for GetReadonlyPnaclFd.
560 // Must be kept in sync with PnaclCanOpenFile() in
561 // components/nacl/browser/nacl_file_host.cc.
562 std::string PnaclComponentURLToFilename(const std::string& url) {
563 // PNaCl component URLs aren't arbitrary URLs; they are always either
564 // generated from ManifestResolveKey or PnaclResources::ReadResourceInfo.
565 // So, it's safe to just use string parsing operations here instead of
566 // URL-parsing ones.
567 DCHECK(StartsWithASCII(url, kPNaClTranslatorBaseUrl, true));
568 std::string r = url.substr(std::string(kPNaClTranslatorBaseUrl).length());
570 // Use white-listed-chars.
571 size_t replace_pos;
572 static const char* white_list = "abcdefghijklmnopqrstuvwxyz0123456789_";
573 replace_pos = r.find_first_not_of(white_list);
574 while(replace_pos != std::string::npos) {
575 r = r.replace(replace_pos, 1, "_");
576 replace_pos = r.find_first_not_of(white_list);
578 return r;
581 PP_FileHandle GetReadonlyPnaclFd(const char* url,
582 bool is_executable,
583 uint64_t* nonce_lo,
584 uint64_t* nonce_hi) {
585 std::string filename = PnaclComponentURLToFilename(url);
586 IPC::PlatformFileForTransit out_fd = IPC::InvalidPlatformFileForTransit();
587 IPC::Sender* sender = content::RenderThread::Get();
588 DCHECK(sender);
589 if (!sender->Send(new NaClHostMsg_GetReadonlyPnaclFD(
590 std::string(filename), is_executable,
591 &out_fd, nonce_lo, nonce_hi))) {
592 return PP_kInvalidFileHandle;
594 if (out_fd == IPC::InvalidPlatformFileForTransit()) {
595 return PP_kInvalidFileHandle;
597 return IPC::PlatformFileForTransitToPlatformFile(out_fd);
600 void GetReadExecPnaclFd(const char* url,
601 PP_NaClFileInfo* out_file_info) {
602 *out_file_info = kInvalidNaClFileInfo;
603 out_file_info->handle = GetReadonlyPnaclFd(url, true /* is_executable */,
604 &out_file_info->token_lo,
605 &out_file_info->token_hi);
608 PP_FileHandle CreateTemporaryFile(PP_Instance instance) {
609 IPC::PlatformFileForTransit transit_fd = IPC::InvalidPlatformFileForTransit();
610 IPC::Sender* sender = content::RenderThread::Get();
611 DCHECK(sender);
612 if (!sender->Send(new NaClHostMsg_NaClCreateTemporaryFile(
613 &transit_fd))) {
614 return PP_kInvalidFileHandle;
617 if (transit_fd == IPC::InvalidPlatformFileForTransit()) {
618 return PP_kInvalidFileHandle;
621 return IPC::PlatformFileForTransitToPlatformFile(transit_fd);
624 int32_t GetNumberOfProcessors() {
625 IPC::Sender* sender = content::RenderThread::Get();
626 DCHECK(sender);
627 int32_t num_processors = 1;
628 return sender->Send(new NaClHostMsg_NaClGetNumProcessors(&num_processors)) ?
629 num_processors : 1;
632 void GetNexeFd(PP_Instance instance,
633 const std::string& pexe_url,
634 uint32_t opt_level,
635 const base::Time& last_modified_time,
636 const std::string& etag,
637 bool has_no_store_header,
638 base::Callback<void(int32_t, bool, PP_FileHandle)> callback) {
639 if (!InitializePnaclResourceHost()) {
640 ppapi::PpapiGlobals::Get()->GetMainThreadMessageLoop()->PostTask(
641 FROM_HERE,
642 base::Bind(callback,
643 static_cast<int32_t>(PP_ERROR_FAILED),
644 false,
645 PP_kInvalidFileHandle));
646 return;
649 PnaclCacheInfo cache_info;
650 cache_info.pexe_url = GURL(pexe_url);
651 // TODO(dschuff): Get this value from the pnacl json file after it
652 // rolls in from NaCl.
653 cache_info.abi_version = 1;
654 cache_info.opt_level = opt_level;
655 cache_info.last_modified = last_modified_time;
656 cache_info.etag = etag;
657 cache_info.has_no_store_header = has_no_store_header;
658 cache_info.sandbox_isa = GetSandboxArch();
659 cache_info.extra_flags = GetCpuFeatures();
661 g_pnacl_resource_host.Get()->RequestNexeFd(
662 GetRoutingID(instance),
663 instance,
664 cache_info,
665 callback);
668 void ReportTranslationFinished(PP_Instance instance,
669 PP_Bool success,
670 int32_t opt_level,
671 int64_t pexe_size,
672 int64_t compile_time_us) {
673 if (success == PP_TRUE) {
674 static const int32_t kUnknownOptLevel = 4;
675 if (opt_level < 0 || opt_level > 3)
676 opt_level = kUnknownOptLevel;
677 HistogramEnumerate("NaCl.Options.PNaCl.OptLevel",
678 opt_level,
679 kUnknownOptLevel + 1);
680 HistogramKBPerSec("NaCl.Perf.PNaClLoadTime.CompileKBPerSec",
681 pexe_size / 1024,
682 compile_time_us);
683 HistogramSizeKB("NaCl.Perf.Size.Pexe", pexe_size / 1024);
685 NexeLoadManager* load_manager = GetNexeLoadManager(instance);
686 if (load_manager) {
687 base::TimeDelta total_time = base::Time::Now() -
688 load_manager->pnacl_start_time();
689 HistogramTimeTranslation("NaCl.Perf.PNaClLoadTime.TotalUncachedTime",
690 total_time.InMilliseconds());
691 HistogramKBPerSec("NaCl.Perf.PNaClLoadTime.TotalUncachedKBPerSec",
692 pexe_size / 1024,
693 total_time.InMicroseconds());
697 // If the resource host isn't initialized, don't try to do that here.
698 // Just return because something is already very wrong.
699 if (g_pnacl_resource_host.Get().get() == NULL)
700 return;
701 g_pnacl_resource_host.Get()->ReportTranslationFinished(instance, success);
703 // Record the pexe size for reporting in a later load event.
704 NaClPluginInstance* nacl_plugin_instance = GetNaClPluginInstance(instance);
705 if (nacl_plugin_instance) {
706 nacl_plugin_instance->pexe_size = pexe_size;
710 PP_FileHandle OpenNaClExecutable(PP_Instance instance,
711 const char* file_url,
712 uint64_t* nonce_lo,
713 uint64_t* nonce_hi) {
714 // Fast path only works for installed file URLs.
715 GURL gurl(file_url);
716 if (!gurl.SchemeIs("chrome-extension"))
717 return PP_kInvalidFileHandle;
719 NexeLoadManager* load_manager = GetNexeLoadManager(instance);
720 DCHECK(load_manager);
721 if (!load_manager)
722 return PP_kInvalidFileHandle;
724 content::PepperPluginInstance* plugin_instance =
725 content::PepperPluginInstance::Get(instance);
726 if (!plugin_instance)
727 return PP_kInvalidFileHandle;
728 // IMPORTANT: Make sure the document can request the given URL. If we don't
729 // check, a malicious app could probe the extension system. This enforces a
730 // same-origin policy which prevents the app from requesting resources from
731 // another app.
732 blink::WebSecurityOrigin security_origin =
733 plugin_instance->GetContainer()->element().document().securityOrigin();
734 if (!security_origin.canRequest(gurl))
735 return PP_kInvalidFileHandle;
737 IPC::PlatformFileForTransit out_fd = IPC::InvalidPlatformFileForTransit();
738 IPC::Sender* sender = content::RenderThread::Get();
739 DCHECK(sender);
740 *nonce_lo = 0;
741 *nonce_hi = 0;
742 base::FilePath file_path;
743 if (!sender->Send(
744 new NaClHostMsg_OpenNaClExecutable(GetRoutingID(instance),
745 GURL(file_url),
746 !load_manager->nonsfi(),
747 &out_fd,
748 nonce_lo,
749 nonce_hi))) {
750 return PP_kInvalidFileHandle;
753 if (out_fd == IPC::InvalidPlatformFileForTransit())
754 return PP_kInvalidFileHandle;
756 return IPC::PlatformFileForTransitToPlatformFile(out_fd);
759 void DispatchEvent(PP_Instance instance,
760 PP_NaClEventType event_type,
761 const char* resource_url,
762 PP_Bool length_is_computable,
763 uint64_t loaded_bytes,
764 uint64_t total_bytes) {
765 ProgressEvent event(event_type,
766 resource_url,
767 PP_ToBool(length_is_computable),
768 loaded_bytes,
769 total_bytes);
770 DispatchProgressEvent(instance, event);
773 void ReportLoadError(PP_Instance instance,
774 PP_NaClError error,
775 const char* error_message) {
776 NexeLoadManager* load_manager = GetNexeLoadManager(instance);
777 if (load_manager)
778 load_manager->ReportLoadError(error, error_message);
781 void InstanceCreated(PP_Instance instance) {
782 InstanceMap& map = g_instance_map.Get();
783 CHECK(map.find(instance) == map.end()); // Sanity check.
784 scoped_ptr<NaClPluginInstance> new_instance(new NaClPluginInstance(instance));
785 map.add(instance, new_instance.Pass());
788 void InstanceDestroyed(PP_Instance instance) {
789 InstanceMap& map = g_instance_map.Get();
790 InstanceMap::iterator iter = map.find(instance);
791 CHECK(iter != map.end());
792 // The erase may call NexeLoadManager's destructor prior to removing it from
793 // the map. In that case, it is possible for the trusted Plugin to re-enter
794 // the NexeLoadManager (e.g., by calling ReportLoadError). Passing out the
795 // NexeLoadManager to a local scoped_ptr just ensures that its entry is gone
796 // from the map prior to the destructor being invoked.
797 scoped_ptr<NaClPluginInstance> temp(map.take(instance));
798 map.erase(iter);
801 PP_Bool NaClDebugEnabledForURL(const char* alleged_nmf_url) {
802 if (!base::CommandLine::ForCurrentProcess()->HasSwitch(
803 switches::kEnableNaClDebug))
804 return PP_FALSE;
805 IPC::Sender* sender = content::RenderThread::Get();
806 DCHECK(sender);
807 bool should_debug = false;
808 return PP_FromBool(
809 sender->Send(new NaClHostMsg_NaClDebugEnabledForURL(GURL(alleged_nmf_url),
810 &should_debug)) &&
811 should_debug);
814 void Vlog(const char* message) {
815 VLOG(1) << message;
818 void InitializePlugin(PP_Instance instance,
819 uint32_t argc,
820 const char* argn[],
821 const char* argv[]) {
822 NexeLoadManager* load_manager = GetNexeLoadManager(instance);
823 DCHECK(load_manager);
824 if (load_manager)
825 load_manager->InitializePlugin(argc, argn, argv);
828 void DownloadManifestToBuffer(PP_Instance instance,
829 struct PP_CompletionCallback callback);
831 bool CreateJsonManifest(PP_Instance instance,
832 const std::string& manifest_url,
833 const std::string& manifest_data);
835 void RequestNaClManifest(PP_Instance instance,
836 PP_CompletionCallback callback) {
837 NexeLoadManager* load_manager = GetNexeLoadManager(instance);
838 DCHECK(load_manager);
839 if (!load_manager) {
840 ppapi::PpapiGlobals::Get()->GetMainThreadMessageLoop()->PostTask(
841 FROM_HERE,
842 base::Bind(callback.func, callback.user_data,
843 static_cast<int32_t>(PP_ERROR_FAILED)));
844 return;
847 std::string url = load_manager->GetManifestURLArgument();
848 if (url.empty() || !load_manager->RequestNaClManifest(url)) {
849 ppapi::PpapiGlobals::Get()->GetMainThreadMessageLoop()->PostTask(
850 FROM_HERE,
851 base::Bind(callback.func, callback.user_data,
852 static_cast<int32_t>(PP_ERROR_FAILED)));
853 return;
856 const GURL& base_url = load_manager->manifest_base_url();
857 if (base_url.SchemeIs("data")) {
858 GURL gurl(base_url);
859 std::string mime_type;
860 std::string charset;
861 std::string data;
862 int32_t error = PP_ERROR_FAILED;
863 if (net::DataURL::Parse(gurl, &mime_type, &charset, &data)) {
864 if (data.size() <= ManifestDownloader::kNaClManifestMaxFileBytes) {
865 if (CreateJsonManifest(instance, base_url.spec(), data))
866 error = PP_OK;
867 } else {
868 load_manager->ReportLoadError(PP_NACL_ERROR_MANIFEST_TOO_LARGE,
869 "manifest file too large.");
871 } else {
872 load_manager->ReportLoadError(PP_NACL_ERROR_MANIFEST_LOAD_URL,
873 "could not load manifest url.");
875 ppapi::PpapiGlobals::Get()->GetMainThreadMessageLoop()->PostTask(
876 FROM_HERE,
877 base::Bind(callback.func, callback.user_data, error));
878 } else {
879 DownloadManifestToBuffer(instance, callback);
883 PP_Var GetManifestBaseURL(PP_Instance instance) {
884 NexeLoadManager* load_manager = GetNexeLoadManager(instance);
885 DCHECK(load_manager);
886 if (!load_manager)
887 return PP_MakeUndefined();
888 const GURL& gurl = load_manager->manifest_base_url();
889 if (!gurl.is_valid())
890 return PP_MakeUndefined();
891 return ppapi::StringVar::StringToPPVar(gurl.spec());
894 void ProcessNaClManifest(PP_Instance instance, const char* program_url) {
895 nacl::NexeLoadManager* load_manager = GetNexeLoadManager(instance);
896 if (load_manager)
897 load_manager->ProcessNaClManifest(program_url);
900 void DownloadManifestToBufferCompletion(PP_Instance instance,
901 struct PP_CompletionCallback callback,
902 base::Time start_time,
903 PP_NaClError pp_nacl_error,
904 const std::string& data);
906 void DownloadManifestToBuffer(PP_Instance instance,
907 struct PP_CompletionCallback callback) {
908 nacl::NexeLoadManager* load_manager = GetNexeLoadManager(instance);
909 DCHECK(load_manager);
910 content::PepperPluginInstance* plugin_instance =
911 content::PepperPluginInstance::Get(instance);
912 if (!load_manager || !plugin_instance) {
913 ppapi::PpapiGlobals::Get()->GetMainThreadMessageLoop()->PostTask(
914 FROM_HERE,
915 base::Bind(callback.func, callback.user_data,
916 static_cast<int32_t>(PP_ERROR_FAILED)));
918 const blink::WebDocument& document =
919 plugin_instance->GetContainer()->element().document();
921 const GURL& gurl = load_manager->manifest_base_url();
922 scoped_ptr<blink::WebURLLoader> url_loader(
923 CreateWebURLLoader(document, gurl));
924 blink::WebURLRequest request = CreateWebURLRequest(document, gurl);
926 // ManifestDownloader deletes itself after invoking the callback.
927 ManifestDownloader* manifest_downloader = new ManifestDownloader(
928 url_loader.Pass(),
929 load_manager->is_installed(),
930 base::Bind(DownloadManifestToBufferCompletion,
931 instance, callback, base::Time::Now()));
932 manifest_downloader->Load(request);
935 void DownloadManifestToBufferCompletion(PP_Instance instance,
936 struct PP_CompletionCallback callback,
937 base::Time start_time,
938 PP_NaClError pp_nacl_error,
939 const std::string& data) {
940 base::TimeDelta download_time = base::Time::Now() - start_time;
941 HistogramTimeSmall("NaCl.Perf.StartupTime.ManifestDownload",
942 download_time.InMilliseconds());
944 nacl::NexeLoadManager* load_manager = GetNexeLoadManager(instance);
945 if (!load_manager) {
946 callback.func(callback.user_data, PP_ERROR_ABORTED);
947 return;
950 int32_t pp_error;
951 switch (pp_nacl_error) {
952 case PP_NACL_ERROR_LOAD_SUCCESS:
953 pp_error = PP_OK;
954 break;
955 case PP_NACL_ERROR_MANIFEST_LOAD_URL:
956 pp_error = PP_ERROR_FAILED;
957 load_manager->ReportLoadError(PP_NACL_ERROR_MANIFEST_LOAD_URL,
958 "could not load manifest url.");
959 break;
960 case PP_NACL_ERROR_MANIFEST_TOO_LARGE:
961 pp_error = PP_ERROR_FILETOOBIG;
962 load_manager->ReportLoadError(PP_NACL_ERROR_MANIFEST_TOO_LARGE,
963 "manifest file too large.");
964 break;
965 case PP_NACL_ERROR_MANIFEST_NOACCESS_URL:
966 pp_error = PP_ERROR_NOACCESS;
967 load_manager->ReportLoadError(PP_NACL_ERROR_MANIFEST_NOACCESS_URL,
968 "access to manifest url was denied.");
969 break;
970 default:
971 NOTREACHED();
972 pp_error = PP_ERROR_FAILED;
973 load_manager->ReportLoadError(PP_NACL_ERROR_MANIFEST_LOAD_URL,
974 "could not load manifest url.");
977 if (pp_error == PP_OK) {
978 std::string base_url = load_manager->manifest_base_url().spec();
979 if (!CreateJsonManifest(instance, base_url, data))
980 pp_error = PP_ERROR_FAILED;
982 callback.func(callback.user_data, pp_error);
985 bool CreateJsonManifest(PP_Instance instance,
986 const std::string& manifest_url,
987 const std::string& manifest_data) {
988 HistogramSizeKB("NaCl.Perf.Size.Manifest",
989 static_cast<int32_t>(manifest_data.length() / 1024));
991 nacl::NexeLoadManager* load_manager = GetNexeLoadManager(instance);
992 if (!load_manager)
993 return false;
995 const char* isa_type;
996 if (load_manager->IsPNaCl())
997 isa_type = kPortableArch;
998 else
999 isa_type = GetSandboxArch();
1001 scoped_ptr<nacl::JsonManifest> j(
1002 new nacl::JsonManifest(
1003 manifest_url.c_str(),
1004 isa_type,
1005 IsNonSFIModeEnabled(),
1006 PP_ToBool(NaClDebugEnabledForURL(manifest_url.c_str()))));
1007 JsonManifest::ErrorInfo error_info;
1008 if (j->Init(manifest_data.c_str(), &error_info)) {
1009 GetNaClPluginInstance(instance)->json_manifest.reset(j.release());
1010 return true;
1012 load_manager->ReportLoadError(error_info.error, error_info.string);
1013 return false;
1016 PP_Bool ManifestGetProgramURL(PP_Instance instance,
1017 PP_Var* pp_full_url,
1018 PP_PNaClOptions* pnacl_options,
1019 PP_Bool* pp_uses_nonsfi_mode) {
1020 nacl::NexeLoadManager* load_manager = GetNexeLoadManager(instance);
1022 JsonManifest* manifest = GetJsonManifest(instance);
1023 if (manifest == NULL)
1024 return PP_FALSE;
1026 bool uses_nonsfi_mode;
1027 std::string full_url;
1028 JsonManifest::ErrorInfo error_info;
1029 if (manifest->GetProgramURL(&full_url, pnacl_options, &uses_nonsfi_mode,
1030 &error_info)) {
1031 *pp_full_url = ppapi::StringVar::StringToPPVar(full_url);
1032 *pp_uses_nonsfi_mode = PP_FromBool(uses_nonsfi_mode);
1033 return PP_TRUE;
1036 if (load_manager)
1037 load_manager->ReportLoadError(error_info.error, error_info.string);
1038 return PP_FALSE;
1041 bool ManifestResolveKey(PP_Instance instance,
1042 bool is_helper_process,
1043 const std::string& key,
1044 std::string* full_url,
1045 PP_PNaClOptions* pnacl_options) {
1046 // For "helper" processes (llc and ld, for PNaCl translation), we resolve
1047 // keys manually as there is no existing .nmf file to parse.
1048 if (is_helper_process) {
1049 pnacl_options->translate = PP_FALSE;
1050 // We can only resolve keys in the files/ namespace.
1051 const std::string kFilesPrefix = "files/";
1052 if (key.find(kFilesPrefix) == std::string::npos) {
1053 nacl::NexeLoadManager* load_manager = GetNexeLoadManager(instance);
1054 if (load_manager)
1055 load_manager->ReportLoadError(PP_NACL_ERROR_MANIFEST_RESOLVE_URL,
1056 "key did not start with files/");
1057 return false;
1059 std::string key_basename = key.substr(kFilesPrefix.length());
1060 *full_url = std::string(kPNaClTranslatorBaseUrl) + GetSandboxArch() + "/" +
1061 key_basename;
1062 return true;
1065 JsonManifest* manifest = GetJsonManifest(instance);
1066 if (manifest == NULL)
1067 return false;
1069 return manifest->ResolveKey(key, full_url, pnacl_options);
1072 PP_Bool GetPNaClResourceInfo(PP_Instance instance,
1073 PP_Var* llc_tool_name,
1074 PP_Var* ld_tool_name) {
1075 static const char kFilename[] = "chrome://pnacl-translator/pnacl.json";
1076 NexeLoadManager* load_manager = GetNexeLoadManager(instance);
1077 DCHECK(load_manager);
1078 if (!load_manager)
1079 return PP_FALSE;
1081 uint64_t nonce_lo = 0;
1082 uint64_t nonce_hi = 0;
1083 base::File file(GetReadonlyPnaclFd(kFilename, false /* is_executable */,
1084 &nonce_lo, &nonce_hi));
1085 if (!file.IsValid()) {
1086 load_manager->ReportLoadError(
1087 PP_NACL_ERROR_PNACL_RESOURCE_FETCH,
1088 "The Portable Native Client (pnacl) component is not "
1089 "installed. Please consult chrome://components for more "
1090 "information.");
1091 return PP_FALSE;
1094 base::File::Info file_info;
1095 if (!file.GetInfo(&file_info)) {
1096 load_manager->ReportLoadError(
1097 PP_NACL_ERROR_PNACL_RESOURCE_FETCH,
1098 std::string("GetPNaClResourceInfo, GetFileInfo failed for: ") +
1099 kFilename);
1100 return PP_FALSE;
1103 if (file_info.size > 1 << 20) {
1104 load_manager->ReportLoadError(
1105 PP_NACL_ERROR_PNACL_RESOURCE_FETCH,
1106 std::string("GetPNaClResourceInfo, file too large: ") + kFilename);
1107 return PP_FALSE;
1110 scoped_ptr<char[]> buffer(new char[file_info.size + 1]);
1111 if (buffer.get() == NULL) {
1112 load_manager->ReportLoadError(
1113 PP_NACL_ERROR_PNACL_RESOURCE_FETCH,
1114 std::string("GetPNaClResourceInfo, couldn't allocate for: ") +
1115 kFilename);
1116 return PP_FALSE;
1119 int rc = file.Read(0, buffer.get(), file_info.size);
1120 if (rc < 0) {
1121 load_manager->ReportLoadError(
1122 PP_NACL_ERROR_PNACL_RESOURCE_FETCH,
1123 std::string("GetPNaClResourceInfo, reading failed for: ") + kFilename);
1124 return PP_FALSE;
1127 // Null-terminate the bytes we we read from the file.
1128 buffer.get()[rc] = 0;
1130 // Expect the JSON file to contain a top-level object (dictionary).
1131 Json::Reader json_reader;
1132 Json::Value json_data;
1133 if (!json_reader.parse(buffer.get(), json_data)) {
1134 load_manager->ReportLoadError(
1135 PP_NACL_ERROR_PNACL_RESOURCE_FETCH,
1136 std::string("Parsing resource info failed: JSON parse error: ") +
1137 json_reader.getFormattedErrorMessages());
1138 return PP_FALSE;
1141 if (!json_data.isObject()) {
1142 load_manager->ReportLoadError(
1143 PP_NACL_ERROR_PNACL_RESOURCE_FETCH,
1144 "Parsing resource info failed: Malformed JSON dictionary");
1145 return PP_FALSE;
1148 if (json_data.isMember("pnacl-llc-name")) {
1149 Json::Value json_name = json_data["pnacl-llc-name"];
1150 if (json_name.isString()) {
1151 std::string llc_tool_name_str = json_name.asString();
1152 *llc_tool_name = ppapi::StringVar::StringToPPVar(llc_tool_name_str);
1156 if (json_data.isMember("pnacl-ld-name")) {
1157 Json::Value json_name = json_data["pnacl-ld-name"];
1158 if (json_name.isString()) {
1159 std::string ld_tool_name_str = json_name.asString();
1160 *ld_tool_name = ppapi::StringVar::StringToPPVar(ld_tool_name_str);
1163 return PP_TRUE;
1166 PP_Var GetCpuFeatureAttrs() {
1167 return ppapi::StringVar::StringToPPVar(GetCpuFeatures());
1170 // Encapsulates some of the state for a call to DownloadNexe to prevent
1171 // argument lists from getting too long.
1172 struct DownloadNexeRequest {
1173 PP_Instance instance;
1174 std::string url;
1175 PP_CompletionCallback callback;
1176 base::Time start_time;
1179 // A utility class to ensure that we don't send progress events more often than
1180 // every 10ms for a given file.
1181 class ProgressEventRateLimiter {
1182 public:
1183 explicit ProgressEventRateLimiter(PP_Instance instance)
1184 : instance_(instance) { }
1186 void ReportProgress(const std::string& url,
1187 int64_t total_bytes_received,
1188 int64_t total_bytes_to_be_received) {
1189 base::Time now = base::Time::Now();
1190 if (now - last_event_ > base::TimeDelta::FromMilliseconds(10)) {
1191 DispatchProgressEvent(instance_,
1192 ProgressEvent(PP_NACL_EVENT_PROGRESS,
1193 url,
1194 total_bytes_to_be_received >= 0,
1195 total_bytes_received,
1196 total_bytes_to_be_received));
1197 last_event_ = now;
1201 private:
1202 PP_Instance instance_;
1203 base::Time last_event_;
1206 void DownloadNexeCompletion(const DownloadNexeRequest& request,
1207 PP_NaClFileInfo* out_file_info,
1208 FileDownloader::Status status,
1209 base::File target_file,
1210 int http_status);
1212 void DownloadNexe(PP_Instance instance,
1213 const char* url,
1214 PP_NaClFileInfo* out_file_info,
1215 PP_CompletionCallback callback) {
1216 CHECK(url);
1217 CHECK(out_file_info);
1218 DownloadNexeRequest request;
1219 request.instance = instance;
1220 request.url = url;
1221 request.callback = callback;
1222 request.start_time = base::Time::Now();
1224 // Try the fast path for retrieving the file first.
1225 PP_FileHandle handle = OpenNaClExecutable(instance,
1226 url,
1227 &out_file_info->token_lo,
1228 &out_file_info->token_hi);
1229 if (handle != PP_kInvalidFileHandle) {
1230 DownloadNexeCompletion(request,
1231 out_file_info,
1232 FileDownloader::SUCCESS,
1233 base::File(handle),
1234 200);
1235 return;
1238 // The fast path didn't work, we'll fetch the file using URLLoader and write
1239 // it to local storage.
1240 base::File target_file(CreateTemporaryFile(instance));
1241 GURL gurl(url);
1243 content::PepperPluginInstance* plugin_instance =
1244 content::PepperPluginInstance::Get(instance);
1245 if (!plugin_instance) {
1246 ppapi::PpapiGlobals::Get()->GetMainThreadMessageLoop()->PostTask(
1247 FROM_HERE,
1248 base::Bind(callback.func, callback.user_data,
1249 static_cast<int32_t>(PP_ERROR_FAILED)));
1251 const blink::WebDocument& document =
1252 plugin_instance->GetContainer()->element().document();
1253 scoped_ptr<blink::WebURLLoader> url_loader(
1254 CreateWebURLLoader(document, gurl));
1255 blink::WebURLRequest url_request = CreateWebURLRequest(document, gurl);
1257 ProgressEventRateLimiter* tracker = new ProgressEventRateLimiter(instance);
1259 // FileDownloader deletes itself after invoking DownloadNexeCompletion.
1260 FileDownloader* file_downloader = new FileDownloader(
1261 url_loader.Pass(),
1262 target_file.Pass(),
1263 base::Bind(&DownloadNexeCompletion, request, out_file_info),
1264 base::Bind(&ProgressEventRateLimiter::ReportProgress,
1265 base::Owned(tracker), std::string(url)));
1266 file_downloader->Load(url_request);
1269 void DownloadNexeCompletion(const DownloadNexeRequest& request,
1270 PP_NaClFileInfo* out_file_info,
1271 FileDownloader::Status status,
1272 base::File target_file,
1273 int http_status) {
1274 int32_t pp_error = FileDownloaderToPepperError(status);
1275 int64_t bytes_read = -1;
1276 if (pp_error == PP_OK && target_file.IsValid()) {
1277 base::File::Info info;
1278 if (target_file.GetInfo(&info))
1279 bytes_read = info.size;
1282 if (bytes_read == -1) {
1283 target_file.Close();
1284 pp_error = PP_ERROR_FAILED;
1287 base::TimeDelta download_time = base::Time::Now() - request.start_time;
1289 NexeLoadManager* load_manager = GetNexeLoadManager(request.instance);
1290 if (load_manager) {
1291 load_manager->NexeFileDidOpen(pp_error,
1292 target_file,
1293 http_status,
1294 bytes_read,
1295 request.url,
1296 download_time);
1299 if (pp_error == PP_OK && target_file.IsValid())
1300 out_file_info->handle = target_file.TakePlatformFile();
1301 else
1302 out_file_info->handle = PP_kInvalidFileHandle;
1304 request.callback.func(request.callback.user_data, pp_error);
1307 void DownloadFileCompletion(
1308 const DownloadFileCallback& callback,
1309 FileDownloader::Status status,
1310 base::File file,
1311 int http_status) {
1312 int32_t pp_error = FileDownloaderToPepperError(status);
1313 PP_NaClFileInfo file_info;
1314 if (pp_error == PP_OK) {
1315 file_info.handle = file.TakePlatformFile();
1316 file_info.token_lo = 0;
1317 file_info.token_hi = 0;
1318 } else {
1319 file_info = kInvalidNaClFileInfo;
1322 callback.Run(pp_error, file_info);
1325 void DownloadFile(PP_Instance instance,
1326 const std::string& url,
1327 const DownloadFileCallback& callback) {
1328 DCHECK(ppapi::PpapiGlobals::Get()->GetMainThreadMessageLoop()->
1329 BelongsToCurrentThread());
1331 NexeLoadManager* load_manager = GetNexeLoadManager(instance);
1332 DCHECK(load_manager);
1333 if (!load_manager) {
1334 base::MessageLoop::current()->PostTask(
1335 FROM_HERE,
1336 base::Bind(callback,
1337 static_cast<int32_t>(PP_ERROR_FAILED),
1338 kInvalidNaClFileInfo));
1339 return;
1342 // Handle special PNaCl support files which are installed on the user's
1343 // machine.
1344 if (url.find(kPNaClTranslatorBaseUrl, 0) == 0) {
1345 PP_NaClFileInfo file_info = kInvalidNaClFileInfo;
1346 PP_FileHandle handle = GetReadonlyPnaclFd(url.c_str(),
1347 false /* is_executable */,
1348 &file_info.token_lo,
1349 &file_info.token_hi);
1350 if (handle == PP_kInvalidFileHandle) {
1351 base::MessageLoop::current()->PostTask(
1352 FROM_HERE,
1353 base::Bind(callback,
1354 static_cast<int32_t>(PP_ERROR_FAILED),
1355 kInvalidNaClFileInfo));
1356 return;
1358 file_info.handle = handle;
1359 base::MessageLoop::current()->PostTask(
1360 FROM_HERE,
1361 base::Bind(callback, static_cast<int32_t>(PP_OK), file_info));
1362 return;
1365 // We have to ensure that this url resolves relative to the plugin base url
1366 // before downloading it.
1367 const GURL& test_gurl = load_manager->plugin_base_url().Resolve(url);
1368 if (!test_gurl.is_valid()) {
1369 base::MessageLoop::current()->PostTask(
1370 FROM_HERE,
1371 base::Bind(callback,
1372 static_cast<int32_t>(PP_ERROR_FAILED),
1373 kInvalidNaClFileInfo));
1374 return;
1377 // Try the fast path for retrieving the file first.
1378 uint64_t file_token_lo = 0;
1379 uint64_t file_token_hi = 0;
1380 PP_FileHandle file_handle = OpenNaClExecutable(instance,
1381 url.c_str(),
1382 &file_token_lo,
1383 &file_token_hi);
1384 if (file_handle != PP_kInvalidFileHandle) {
1385 PP_NaClFileInfo file_info;
1386 file_info.handle = file_handle;
1387 file_info.token_lo = file_token_lo;
1388 file_info.token_hi = file_token_hi;
1389 base::MessageLoop::current()->PostTask(
1390 FROM_HERE,
1391 base::Bind(callback, static_cast<int32_t>(PP_OK), file_info));
1392 return;
1395 // The fast path didn't work, we'll fetch the file using URLLoader and write
1396 // it to local storage.
1397 base::File target_file(CreateTemporaryFile(instance));
1398 GURL gurl(url);
1400 content::PepperPluginInstance* plugin_instance =
1401 content::PepperPluginInstance::Get(instance);
1402 if (!plugin_instance) {
1403 base::MessageLoop::current()->PostTask(
1404 FROM_HERE,
1405 base::Bind(callback,
1406 static_cast<int32_t>(PP_ERROR_FAILED),
1407 kInvalidNaClFileInfo));
1409 const blink::WebDocument& document =
1410 plugin_instance->GetContainer()->element().document();
1411 scoped_ptr<blink::WebURLLoader> url_loader(
1412 CreateWebURLLoader(document, gurl));
1413 blink::WebURLRequest url_request = CreateWebURLRequest(document, gurl);
1415 ProgressEventRateLimiter* tracker = new ProgressEventRateLimiter(instance);
1417 // FileDownloader deletes itself after invoking DownloadNexeCompletion.
1418 FileDownloader* file_downloader = new FileDownloader(
1419 url_loader.Pass(),
1420 target_file.Pass(),
1421 base::Bind(&DownloadFileCompletion, callback),
1422 base::Bind(&ProgressEventRateLimiter::ReportProgress,
1423 base::Owned(tracker), std::string(url)));
1424 file_downloader->Load(url_request);
1427 void ReportSelLdrStatus(PP_Instance instance,
1428 int32_t load_status,
1429 int32_t max_status) {
1430 HistogramEnumerate("NaCl.LoadStatus.SelLdr", load_status, max_status);
1431 NexeLoadManager* load_manager = GetNexeLoadManager(instance);
1432 DCHECK(load_manager);
1433 if (!load_manager)
1434 return;
1436 // Gather data to see if being installed changes load outcomes.
1437 const char* name = load_manager->is_installed() ?
1438 "NaCl.LoadStatus.SelLdr.InstalledApp" :
1439 "NaCl.LoadStatus.SelLdr.NotInstalledApp";
1440 HistogramEnumerate(name, load_status, max_status);
1443 void LogTranslateTime(const char* histogram_name,
1444 int64_t time_in_us) {
1445 ppapi::PpapiGlobals::Get()->GetMainThreadMessageLoop()->PostTask(
1446 FROM_HERE,
1447 base::Bind(&HistogramTimeTranslation,
1448 std::string(histogram_name),
1449 time_in_us / 1000));
1452 void SetPNaClStartTime(PP_Instance instance) {
1453 NexeLoadManager* load_manager = GetNexeLoadManager(instance);
1454 if (load_manager)
1455 load_manager->set_pnacl_start_time(base::Time::Now());
1458 // PexeDownloader is responsible for deleting itself when the download
1459 // finishes.
1460 class PexeDownloader : public blink::WebURLLoaderClient {
1461 public:
1462 PexeDownloader(PP_Instance instance,
1463 scoped_ptr<blink::WebURLLoader> url_loader,
1464 const std::string& pexe_url,
1465 int32_t pexe_opt_level,
1466 const PPP_PexeStreamHandler* stream_handler,
1467 void* stream_handler_user_data)
1468 : instance_(instance),
1469 url_loader_(url_loader.Pass()),
1470 pexe_url_(pexe_url),
1471 pexe_opt_level_(pexe_opt_level),
1472 stream_handler_(stream_handler),
1473 stream_handler_user_data_(stream_handler_user_data),
1474 success_(false),
1475 expected_content_length_(-1),
1476 weak_factory_(this) { }
1478 void Load(const blink::WebURLRequest& request) {
1479 url_loader_->loadAsynchronously(request, this);
1482 private:
1483 virtual void didReceiveResponse(blink::WebURLLoader* loader,
1484 const blink::WebURLResponse& response) {
1485 success_ = (response.httpStatusCode() == 200);
1486 if (!success_)
1487 return;
1489 expected_content_length_ = response.expectedContentLength();
1491 // Defer loading after receiving headers. This is because we may already
1492 // have a cached translated nexe, so check for that now.
1493 url_loader_->setDefersLoading(true);
1495 std::string etag = response.httpHeaderField("etag").utf8();
1496 std::string last_modified =
1497 response.httpHeaderField("last-modified").utf8();
1498 base::Time last_modified_time;
1499 base::Time::FromString(last_modified.c_str(), &last_modified_time);
1501 bool has_no_store_header = false;
1502 std::string cache_control =
1503 response.httpHeaderField("cache-control").utf8();
1505 std::vector<std::string> values;
1506 base::SplitString(cache_control, ',', &values);
1507 for (std::vector<std::string>::const_iterator it = values.begin();
1508 it != values.end();
1509 ++it) {
1510 if (base::StringToLowerASCII(*it) == "no-store")
1511 has_no_store_header = true;
1514 GetNexeFd(instance_,
1515 pexe_url_,
1516 pexe_opt_level_,
1517 last_modified_time,
1518 etag,
1519 has_no_store_header,
1520 base::Bind(&PexeDownloader::didGetNexeFd,
1521 weak_factory_.GetWeakPtr()));
1524 virtual void didGetNexeFd(int32_t pp_error,
1525 bool cache_hit,
1526 PP_FileHandle file_handle) {
1527 if (!content::PepperPluginInstance::Get(instance_)) {
1528 delete this;
1529 return;
1532 HistogramEnumerate("NaCl.Perf.PNaClCache.IsHit", cache_hit, 2);
1533 if (cache_hit) {
1534 stream_handler_->DidCacheHit(stream_handler_user_data_, file_handle);
1536 // We delete the PexeDownloader at this point since we successfully got a
1537 // cached, translated nexe.
1538 delete this;
1539 return;
1541 stream_handler_->DidCacheMiss(stream_handler_user_data_,
1542 expected_content_length_,
1543 file_handle);
1545 // No translated nexe was found in the cache, so we should download the
1546 // file to start streaming it.
1547 url_loader_->setDefersLoading(false);
1550 virtual void didReceiveData(blink::WebURLLoader* loader,
1551 const char* data,
1552 int data_length,
1553 int encoded_data_length) {
1554 if (content::PepperPluginInstance::Get(instance_)) {
1555 // Stream the data we received to the stream callback.
1556 stream_handler_->DidStreamData(stream_handler_user_data_,
1557 data,
1558 data_length);
1562 virtual void didFinishLoading(blink::WebURLLoader* loader,
1563 double finish_time,
1564 int64_t total_encoded_data_length) {
1565 int32_t result = success_ ? PP_OK : PP_ERROR_FAILED;
1567 if (content::PepperPluginInstance::Get(instance_))
1568 stream_handler_->DidFinishStream(stream_handler_user_data_, result);
1569 delete this;
1572 virtual void didFail(blink::WebURLLoader* loader,
1573 const blink::WebURLError& error) {
1574 success_ = false;
1577 PP_Instance instance_;
1578 scoped_ptr<blink::WebURLLoader> url_loader_;
1579 std::string pexe_url_;
1580 int32_t pexe_opt_level_;
1581 const PPP_PexeStreamHandler* stream_handler_;
1582 void* stream_handler_user_data_;
1583 bool success_;
1584 int64_t expected_content_length_;
1585 base::WeakPtrFactory<PexeDownloader> weak_factory_;
1588 void StreamPexe(PP_Instance instance,
1589 const char* pexe_url,
1590 int32_t opt_level,
1591 const PPP_PexeStreamHandler* handler,
1592 void* handler_user_data) {
1593 content::PepperPluginInstance* plugin_instance =
1594 content::PepperPluginInstance::Get(instance);
1595 if (!plugin_instance) {
1596 base::MessageLoop::current()->PostTask(
1597 FROM_HERE,
1598 base::Bind(handler->DidFinishStream,
1599 handler_user_data,
1600 static_cast<int32_t>(PP_ERROR_FAILED)));
1601 return;
1604 GURL gurl(pexe_url);
1605 const blink::WebDocument& document =
1606 plugin_instance->GetContainer()->element().document();
1607 scoped_ptr<blink::WebURLLoader> url_loader(
1608 CreateWebURLLoader(document, gurl));
1609 PexeDownloader* downloader = new PexeDownloader(instance,
1610 url_loader.Pass(),
1611 pexe_url,
1612 opt_level,
1613 handler,
1614 handler_user_data);
1616 blink::WebURLRequest url_request = CreateWebURLRequest(document, gurl);
1617 // Mark the request as requesting a PNaCl bitcode file,
1618 // so that component updater can detect this user action.
1619 url_request.addHTTPHeaderField(
1620 blink::WebString::fromUTF8("Accept"),
1621 blink::WebString::fromUTF8("application/x-pnacl, */*"));
1622 url_request.setRequestContext(blink::WebURLRequest::RequestContextObject);
1623 downloader->Load(url_request);
1626 const PPB_NaCl_Private nacl_interface = {
1627 &LaunchSelLdr,
1628 &UrandomFD,
1629 &BrokerDuplicateHandle,
1630 &GetReadExecPnaclFd,
1631 &CreateTemporaryFile,
1632 &GetNumberOfProcessors,
1633 &ReportTranslationFinished,
1634 &DispatchEvent,
1635 &ReportLoadError,
1636 &InstanceCreated,
1637 &InstanceDestroyed,
1638 &GetSandboxArch,
1639 &Vlog,
1640 &InitializePlugin,
1641 &RequestNaClManifest,
1642 &GetManifestBaseURL,
1643 &ProcessNaClManifest,
1644 &ManifestGetProgramURL,
1645 &GetPNaClResourceInfo,
1646 &GetCpuFeatureAttrs,
1647 &DownloadNexe,
1648 &ReportSelLdrStatus,
1649 &LogTranslateTime,
1650 &SetPNaClStartTime,
1651 &StreamPexe
1654 } // namespace
1656 const PPB_NaCl_Private* GetNaClPrivateInterface() {
1657 return &nacl_interface;
1660 } // namespace nacl