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