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