1 // Copyright (c) 2012 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 #ifdef _MSC_VER 6 // Do not warn about use of std::copy with raw pointers. 7 #pragma warning(disable : 4996) 8 #endif 9 10 #include "ppapi/native_client/src/trusted/plugin/plugin.h" 11 12 #include <sys/stat.h> 13 #include <sys/types.h> 14 15 #include <algorithm> 16 #include <deque> 17 #include <string> 18 #include <vector> 19 20 #include "native_client/src/include/nacl_base.h" 21 #include "native_client/src/include/nacl_macros.h" 22 #include "native_client/src/include/nacl_scoped_ptr.h" 23 #include "native_client/src/include/nacl_string.h" 24 #include "native_client/src/include/portability.h" 25 #include "native_client/src/include/portability_io.h" 26 #include "native_client/src/include/portability_string.h" 27 #include "native_client/src/shared/platform/nacl_check.h" 28 #include "native_client/src/trusted/desc/nacl_desc_wrapper.h" 29 #include "native_client/src/trusted/nonnacl_util/sel_ldr_launcher.h" 30 #include "native_client/src/trusted/service_runtime/nacl_error_code.h" 31 32 #include "ppapi/c/pp_errors.h" 33 #include "ppapi/c/ppb_console.h" 34 #include "ppapi/c/ppb_var.h" 35 #include "ppapi/c/ppp_instance.h" 36 #include "ppapi/c/private/ppb_nacl_private.h" 37 #include "ppapi/c/private/ppb_uma_private.h" 38 #include "ppapi/cpp/dev/url_util_dev.h" 39 #include "ppapi/cpp/module.h" 40 #include "ppapi/cpp/text_input_controller.h" 41 42 #include "ppapi/native_client/src/trusted/plugin/file_utils.h" 43 #include "ppapi/native_client/src/trusted/plugin/json_manifest.h" 44 #include "ppapi/native_client/src/trusted/plugin/nacl_entry_points.h" 45 #include "ppapi/native_client/src/trusted/plugin/nacl_subprocess.h" 46 #include "ppapi/native_client/src/trusted/plugin/nexe_arch.h" 47 #include "ppapi/native_client/src/trusted/plugin/plugin_error.h" 48 #include "ppapi/native_client/src/trusted/plugin/scriptable_plugin.h" 49 #include "ppapi/native_client/src/trusted/plugin/service_runtime.h" 50 #include "ppapi/native_client/src/trusted/plugin/utility.h" 51 52 namespace plugin { 53 54 namespace { 55 56 const char* const kTypeAttribute = "type"; 57 // The "src" attribute of the <embed> tag. The value is expected to be either 58 // a URL or URI pointing to the manifest file (which is expected to contain 59 // JSON matching ISAs with .nexe URLs). 60 const char* const kSrcManifestAttribute = "src"; 61 // The "nacl" attribute of the <embed> tag. We use the value of this attribute 62 // to find the manifest file when NaCl is registered as a plug-in for another 63 // MIME type because the "src" attribute is used to supply us with the resource 64 // of that MIME type that we're supposed to display. 65 const char* const kNaClManifestAttribute = "nacl"; 66 // The pseudo-ISA used to indicate portable native client. 67 const char* const kPortableISA = "portable"; 68 // This is a pretty arbitrary limit on the byte size of the NaCl manfest file. 69 // Note that the resulting string object has to have at least one byte extra 70 // for the null termination character. 71 const size_t kNaClManifestMaxFileBytes = 1024 * 1024; 72 73 // Define an argument name to enable 'dev' interfaces. To make sure it doesn't 74 // collide with any user-defined HTML attribute, make the first character '@'. 75 const char* const kDevAttribute = "@dev"; 76 77 // URL schemes that we treat in special ways. 78 const char* const kChromeExtensionUriScheme = "chrome-extension"; 79 const char* const kDataUriScheme = "data"; 80 81 // Up to 20 seconds 82 const int64_t kTimeSmallMin = 1; // in ms 83 const int64_t kTimeSmallMax = 20000; // in ms 84 const uint32_t kTimeSmallBuckets = 100; 85 86 // Up to 3 minutes, 20 seconds 87 const int64_t kTimeMediumMin = 10; // in ms 88 const int64_t kTimeMediumMax = 200000; // in ms 89 const uint32_t kTimeMediumBuckets = 100; 90 91 // Up to 33 minutes. 92 const int64_t kTimeLargeMin = 100; // in ms 93 const int64_t kTimeLargeMax = 2000000; // in ms 94 const uint32_t kTimeLargeBuckets = 100; 95 96 const int64_t kSizeKBMin = 1; 97 const int64_t kSizeKBMax = 512*1024; // very large .nexe 98 const uint32_t kSizeKBBuckets = 100; 99 100 const PPB_NaCl_Private* GetNaClInterface() { 101 pp::Module *module = pp::Module::Get(); 102 CHECK(module); 103 return static_cast<const PPB_NaCl_Private*>( 104 module->GetBrowserInterface(PPB_NACL_PRIVATE_INTERFACE)); 105 } 106 107 const PPB_UMA_Private* GetUMAInterface() { 108 pp::Module *module = pp::Module::Get(); 109 CHECK(module); 110 return static_cast<const PPB_UMA_Private*>( 111 module->GetBrowserInterface(PPB_UMA_PRIVATE_INTERFACE)); 112 } 113 114 void HistogramTimeSmall(const std::string& name, int64_t ms) { 115 if (ms < 0) return; 116 117 const PPB_UMA_Private* ptr = GetUMAInterface(); 118 if (ptr == NULL) return; 119 120 ptr->HistogramCustomTimes(pp::Var(name).pp_var(), 121 ms, 122 kTimeSmallMin, kTimeSmallMax, 123 kTimeSmallBuckets); 124 } 125 126 void HistogramTimeMedium(const std::string& name, int64_t ms) { 127 if (ms < 0) return; 128 129 const PPB_UMA_Private* ptr = GetUMAInterface(); 130 if (ptr == NULL) return; 131 132 ptr->HistogramCustomTimes(pp::Var(name).pp_var(), 133 ms, 134 kTimeMediumMin, kTimeMediumMax, 135 kTimeMediumBuckets); 136 } 137 138 void HistogramTimeLarge(const std::string& name, int64_t ms) { 139 if (ms < 0) return; 140 141 const PPB_UMA_Private* ptr = GetUMAInterface(); 142 if (ptr == NULL) return; 143 144 ptr->HistogramCustomTimes(pp::Var(name).pp_var(), 145 ms, 146 kTimeLargeMin, kTimeLargeMax, 147 kTimeLargeBuckets); 148 } 149 150 void HistogramSizeKB(const std::string& name, int32_t sample) { 151 if (sample < 0) return; 152 153 const PPB_UMA_Private* ptr = GetUMAInterface(); 154 if (ptr == NULL) return; 155 156 ptr->HistogramCustomCounts(pp::Var(name).pp_var(), 157 sample, 158 kSizeKBMin, kSizeKBMax, 159 kSizeKBBuckets); 160 } 161 162 void HistogramEnumerate(const std::string& name, int sample, int maximum, 163 int out_of_range_replacement) { 164 if (sample < 0 || sample >= maximum) { 165 if (out_of_range_replacement < 0) 166 // No replacement for bad input, abort. 167 return; 168 else 169 // Use a specific value to signal a bad input. 170 sample = out_of_range_replacement; 171 } 172 const PPB_UMA_Private* ptr = GetUMAInterface(); 173 if (ptr == NULL) return; 174 ptr->HistogramEnumeration(pp::Var(name).pp_var(), sample, maximum); 175 } 176 177 void HistogramEnumerateOsArch(const std::string& sandbox_isa) { 178 enum NaClOSArch { 179 kNaClLinux32 = 0, 180 kNaClLinux64, 181 kNaClLinuxArm, 182 kNaClMac32, 183 kNaClMac64, 184 kNaClMacArm, 185 kNaClWin32, 186 kNaClWin64, 187 kNaClWinArm, 188 kNaClOSArchMax 189 }; 190 191 NaClOSArch os_arch = kNaClOSArchMax; 192 #if NACL_LINUX 193 os_arch = kNaClLinux32; 194 #elif NACL_OSX 195 os_arch = kNaClMac32; 196 #elif NACL_WINDOWS 197 os_arch = kNaClWin32; 198 #endif 199 200 if (sandbox_isa == "x86-64") 201 os_arch = static_cast<NaClOSArch>(os_arch + 1); 202 if (sandbox_isa == "arm") 203 os_arch = static_cast<NaClOSArch>(os_arch + 2); 204 205 HistogramEnumerate("NaCl.Client.OSArch", os_arch, kNaClOSArchMax, -1); 206 } 207 208 void HistogramEnumerateLoadStatus(PluginErrorCode error_code, 209 bool is_installed) { 210 HistogramEnumerate("NaCl.LoadStatus.Plugin", error_code, ERROR_MAX, 211 ERROR_UNKNOWN); 212 213 // Gather data to see if being installed changes load outcomes. 214 const char* name = is_installed ? "NaCl.LoadStatus.Plugin.InstalledApp" : 215 "NaCl.LoadStatus.Plugin.NotInstalledApp"; 216 HistogramEnumerate(name, error_code, ERROR_MAX, 217 ERROR_UNKNOWN); 218 } 219 220 void HistogramEnumerateSelLdrLoadStatus(NaClErrorCode error_code, 221 bool is_installed) { 222 HistogramEnumerate("NaCl.LoadStatus.SelLdr", error_code, NACL_ERROR_CODE_MAX, 223 LOAD_STATUS_UNKNOWN); 224 225 // Gather data to see if being installed changes load outcomes. 226 const char* name = is_installed ? "NaCl.LoadStatus.SelLdr.InstalledApp" : 227 "NaCl.LoadStatus.SelLdr.NotInstalledApp"; 228 HistogramEnumerate(name, error_code, NACL_ERROR_CODE_MAX, 229 LOAD_STATUS_UNKNOWN); 230 } 231 232 void HistogramEnumerateManifestIsDataURI(bool is_data_uri) { 233 HistogramEnumerate("NaCl.Manifest.IsDataURI", is_data_uri, 2, -1); 234 } 235 236 void HistogramHTTPStatusCode(const std::string& name, int status) { 237 // Log the status codes in rough buckets - 1XX, 2XX, etc. 238 int sample = status / 100; 239 // HTTP status codes only go up to 5XX, using "6" to indicate an internal 240 // error. 241 // Note: installed files may have "0" for a status code. 242 if (status < 0 || status >= 600) 243 sample = 6; 244 HistogramEnumerate(name, sample, 7, 6); 245 } 246 247 } // namespace 248 249 bool Plugin::EarlyInit(int argc, const char* argn[], const char* argv[]) { 250 PLUGIN_PRINTF(("Plugin::EarlyInit (instance=%p)\n", 251 static_cast<void*>(this))); 252 253 #ifdef NACL_OSX 254 // TODO(kochi): For crbug.com/102808, this is a stopgap solution for Lion 255 // until we expose IME API to .nexe. This disables any IME interference 256 // against key inputs, so you cannot use off-the-spot IME input for NaCl apps. 257 // This makes discrepancy among platforms and therefore we should remove 258 // this hack when IME API is made available. 259 // The default for non-Mac platforms is still off-the-spot IME mode. 260 pp::TextInputController(this).SetTextInputType(PP_TEXTINPUT_TYPE_NONE); 261 #endif 262 263 // Remember the embed/object argn/argv pairs. 264 argn_ = new char*[argc]; 265 argv_ = new char*[argc]; 266 argc_ = 0; 267 for (int i = 0; i < argc; ++i) { 268 if (NULL != argn_ && NULL != argv_) { 269 argn_[argc_] = strdup(argn[i]); 270 argv_[argc_] = strdup(argv[i]); 271 if (NULL == argn_[argc_] || NULL == argv_[argc_]) { 272 // Give up on passing arguments. 273 free(argn_[argc_]); 274 free(argv_[argc_]); 275 continue; 276 } 277 ++argc_; 278 } 279 } 280 // TODO(sehr): this leaks strings if there is a subsequent failure. 281 282 // Set up the factory used to produce DescWrappers. 283 wrapper_factory_ = new nacl::DescWrapperFactory(); 284 if (NULL == wrapper_factory_) { 285 return false; 286 } 287 PLUGIN_PRINTF(("Plugin::Init (wrapper_factory=%p)\n", 288 static_cast<void*>(wrapper_factory_))); 289 290 PLUGIN_PRINTF(("Plugin::Init (return 1)\n")); 291 // Return success. 292 return true; 293 } 294 295 void Plugin::ShutDownSubprocesses() { 296 PLUGIN_PRINTF(("Plugin::ShutDownSubprocesses (this=%p)\n", 297 static_cast<void*>(this))); 298 PLUGIN_PRINTF(("Plugin::ShutDownSubprocesses (%s)\n", 299 main_subprocess_.detailed_description().c_str())); 300 301 // Shut down service runtime. This must be done before all other calls so 302 // they don't block forever when waiting for the upcall thread to exit. 303 main_subprocess_.Shutdown(); 304 305 PLUGIN_PRINTF(("Plugin::ShutDownSubprocess (this=%p, return)\n", 306 static_cast<void*>(this))); 307 } 308 309 void Plugin::StartSelLdrOnMainThread(int32_t pp_error, 310 ServiceRuntime* service_runtime, 311 const SelLdrStartParams& params, 312 bool* success) { 313 if (pp_error != PP_OK) { 314 PLUGIN_PRINTF(("Plugin::StartSelLdrOnMainThread: non-PP_OK arg " 315 "-- SHOULD NOT HAPPEN\n")); 316 *success = false; 317 return; 318 } 319 *success = service_runtime->StartSelLdr(params); 320 // Signal outside of StartSelLdr here, so that the write to *success 321 // is done before signaling. 322 service_runtime->SignalStartSelLdrDone(); 323 } 324 325 bool Plugin::LoadNaClModuleCommon(nacl::DescWrapper* wrapper, 326 NaClSubprocess* subprocess, 327 const Manifest* manifest, 328 bool should_report_uma, 329 const SelLdrStartParams& params, 330 const pp::CompletionCallback& init_done_cb, 331 const pp::CompletionCallback& crash_cb) { 332 ServiceRuntime* new_service_runtime = 333 new ServiceRuntime(this, manifest, should_report_uma, init_done_cb, 334 crash_cb); 335 subprocess->set_service_runtime(new_service_runtime); 336 PLUGIN_PRINTF(("Plugin::LoadNaClModuleCommon (service_runtime=%p)\n", 337 static_cast<void*>(new_service_runtime))); 338 if (NULL == new_service_runtime) { 339 params.error_info->SetReport( 340 ERROR_SEL_LDR_INIT, 341 "sel_ldr init failure " + subprocess->description()); 342 return false; 343 } 344 345 // Now start the SelLdr instance. This must be created on the main thread. 346 pp::Core* core = pp::Module::Get()->core(); 347 bool service_runtime_started; 348 if (core->IsMainThread()) { 349 StartSelLdrOnMainThread(PP_OK, new_service_runtime, params, 350 &service_runtime_started); 351 } else { 352 pp::CompletionCallback callback = 353 callback_factory_.NewCallback(&Plugin::StartSelLdrOnMainThread, 354 new_service_runtime, params, 355 &service_runtime_started); 356 core->CallOnMainThread(0, callback, 0); 357 new_service_runtime->WaitForSelLdrStart(); 358 } 359 PLUGIN_PRINTF(("Plugin::LoadNaClModuleCommon (service_runtime_started=%d)\n", 360 service_runtime_started)); 361 if (!service_runtime_started) { 362 return false; 363 } 364 365 // Now actually load the nexe, which can happen on a background thread. 366 bool nexe_loaded = new_service_runtime->LoadNexeAndStart(wrapper, 367 params.error_info, 368 crash_cb); 369 PLUGIN_PRINTF(("Plugin::LoadNaClModuleCommon (nexe_loaded=%d)\n", 370 nexe_loaded)); 371 if (!nexe_loaded) { 372 return false; 373 } 374 return true; 375 } 376 377 bool Plugin::LoadNaClModule(nacl::DescWrapper* wrapper, 378 ErrorInfo* error_info, 379 bool enable_dyncode_syscalls, 380 bool enable_exception_handling, 381 bool enable_crash_throttling, 382 const pp::CompletionCallback& init_done_cb, 383 const pp::CompletionCallback& crash_cb) { 384 // Before forking a new sel_ldr process, ensure that we do not leak 385 // the ServiceRuntime object for an existing subprocess, and that any 386 // associated listener threads do not go unjoined because if they 387 // outlive the Plugin object, they will not be memory safe. 388 ShutDownSubprocesses(); 389 SelLdrStartParams params(manifest_base_url(), 390 error_info, 391 true /* uses_irt */, 392 true /* uses_ppapi */, 393 enable_dev_interfaces_, 394 enable_dyncode_syscalls, 395 enable_exception_handling, 396 enable_crash_throttling); 397 if (!LoadNaClModuleCommon(wrapper, &main_subprocess_, manifest_.get(), 398 true /* should_report_uma */, 399 params, init_done_cb, crash_cb)) { 400 return false; 401 } 402 PLUGIN_PRINTF(("Plugin::LoadNaClModule (%s)\n", 403 main_subprocess_.detailed_description().c_str())); 404 return true; 405 } 406 407 bool Plugin::LoadNaClModuleContinuationIntern(ErrorInfo* error_info) { 408 if (!main_subprocess_.StartSrpcServices()) { 409 // The NaCl process probably crashed. On Linux, a crash causes this error, 410 // while on other platforms, the error is detected below, when we attempt to 411 // start the proxy. Report a module initialization error here, to make it 412 // less confusing for developers. 413 NaClLog(LOG_ERROR, "LoadNaClModuleContinuationIntern: " 414 "StartSrpcServices failed\n"); 415 error_info->SetReport(ERROR_START_PROXY_MODULE, 416 "could not initialize module."); 417 return false; 418 } 419 PP_ExternalPluginResult ipc_result = 420 nacl_interface_->StartPpapiProxy(pp_instance()); 421 if (ipc_result == PP_EXTERNAL_PLUGIN_OK) { 422 // Log the amound of time that has passed between the trusted plugin being 423 // initialized and the untrusted plugin being initialized. This is 424 // (roughly) the cost of using NaCl, in terms of startup time. 425 HistogramStartupTimeMedium( 426 "NaCl.Perf.StartupTime.NaClOverhead", 427 static_cast<float>(NaClGetTimeOfDayMicroseconds() - init_time_) 428 / NACL_MICROS_PER_MILLI); 429 } else if (ipc_result == PP_EXTERNAL_PLUGIN_ERROR_MODULE) { 430 NaClLog(LOG_ERROR, "LoadNaClModuleContinuationIntern: " 431 "Got PP_EXTERNAL_PLUGIN_ERROR_MODULE\n"); 432 error_info->SetReport(ERROR_START_PROXY_MODULE, 433 "could not initialize module."); 434 return false; 435 } else if (ipc_result == PP_EXTERNAL_PLUGIN_ERROR_INSTANCE) { 436 error_info->SetReport(ERROR_START_PROXY_INSTANCE, 437 "could not create instance."); 438 return false; 439 } 440 PLUGIN_PRINTF(("Plugin::LoadNaClModule (%s)\n", 441 main_subprocess_.detailed_description().c_str())); 442 return true; 443 } 444 445 NaClSubprocess* Plugin::LoadHelperNaClModule(nacl::DescWrapper* wrapper, 446 const Manifest* manifest, 447 ErrorInfo* error_info) { 448 nacl::scoped_ptr<NaClSubprocess> nacl_subprocess( 449 new NaClSubprocess("helper module", NULL, NULL)); 450 if (NULL == nacl_subprocess.get()) { 451 error_info->SetReport(ERROR_SEL_LDR_INIT, 452 "unable to allocate helper subprocess."); 453 return NULL; 454 } 455 456 // Do not report UMA stats for translator-related nexes. 457 // TODO(sehr): define new UMA stats for translator related nexe events. 458 // NOTE: The PNaCl translator nexes are not built to use the IRT. This is 459 // done to save on address space and swap space. 460 // TODO(jvoung): See if we still need the uses_ppapi variable, now that 461 // LaunchSelLdr always happens on the main thread. 462 SelLdrStartParams params(manifest_base_url(), 463 error_info, 464 false /* uses_irt */, 465 false /* uses_ppapi */, 466 enable_dev_interfaces_, 467 false /* enable_dyncode_syscalls */, 468 false /* enable_exception_handling */, 469 true /* enable_crash_throttling */); 470 if (!LoadNaClModuleCommon(wrapper, nacl_subprocess.get(), manifest, 471 false /* should_report_uma */, 472 params, 473 pp::BlockUntilComplete(), 474 pp::BlockUntilComplete())) { 475 return NULL; 476 } 477 // We need not wait for the init_done callback. We can block 478 // here in StartSrpcServices, since helper NaCl modules 479 // are spawned from a private thread. 480 // 481 // TODO(bsy): if helper module crashes, we should abort. 482 // crash_cb is not used here, so we are relying on crashes 483 // being detected in StartSrpcServices or later. 484 // 485 // NB: More refactoring might be needed, however, if helper 486 // NaCl modules have their own manifest. Currently the 487 // manifest is a per-plugin-instance object, not a per 488 // NaClSubprocess object. 489 if (!nacl_subprocess->StartSrpcServices()) { 490 error_info->SetReport(ERROR_SRPC_CONNECTION_FAIL, 491 "SRPC connection failure for " + 492 nacl_subprocess->description()); 493 return NULL; 494 } 495 496 PLUGIN_PRINTF(("Plugin::LoadHelperNaClModule (%s)\n", 497 nacl_subprocess.get()->detailed_description().c_str())); 498 499 return nacl_subprocess.release(); 500 } 501 502 char* Plugin::LookupArgument(const char* key) { 503 char** keys = argn_; 504 for (int ii = 0, len = argc_; ii < len; ++ii) { 505 if (!strcmp(keys[ii], key)) { 506 return argv_[ii]; 507 } 508 } 509 return NULL; 510 } 511 512 class ProgressEvent { 513 public: 514 ProgressEvent(PP_NaClEventType event_type, 515 const nacl::string& url, 516 Plugin::LengthComputable length_computable, 517 uint64_t loaded_bytes, 518 uint64_t total_bytes) : 519 event_type_(event_type), 520 url_(url), 521 length_computable_(length_computable), 522 loaded_bytes_(loaded_bytes), 523 total_bytes_(total_bytes) { } 524 PP_NaClEventType event_type() const { return event_type_; } 525 const char* url() const { return url_.c_str(); } 526 Plugin::LengthComputable length_computable() const { 527 return length_computable_; 528 } 529 uint64_t loaded_bytes() const { return loaded_bytes_; } 530 uint64_t total_bytes() const { return total_bytes_; } 531 532 private: 533 PP_NaClEventType event_type_; 534 nacl::string url_; 535 Plugin::LengthComputable length_computable_; 536 uint64_t loaded_bytes_; 537 uint64_t total_bytes_; 538 }; 539 540 const char* const Plugin::kNaClMIMEType = "application/x-nacl"; 541 const char* const Plugin::kPnaclMIMEType = "application/x-pnacl"; 542 543 bool Plugin::NexeIsContentHandler() const { 544 // Tests if the MIME type is not a NaCl MIME type. 545 // If the MIME type is foreign, then this NEXE is being used as a content 546 // type handler rather than directly by an HTML document. 547 return 548 !mime_type().empty() && 549 mime_type() != kNaClMIMEType && 550 mime_type() != kPnaclMIMEType; 551 } 552 553 554 Plugin* Plugin::New(PP_Instance pp_instance) { 555 PLUGIN_PRINTF(("Plugin::New (pp_instance=%" NACL_PRId32 ")\n", pp_instance)); 556 Plugin* plugin = new Plugin(pp_instance); 557 PLUGIN_PRINTF(("Plugin::New (plugin=%p)\n", static_cast<void*>(plugin))); 558 return plugin; 559 } 560 561 562 // All failures of this function will show up as "Missing Plugin-in", so 563 // there is no need to log to JS console that there was an initialization 564 // failure. Note that module loading functions will log their own errors. 565 bool Plugin::Init(uint32_t argc, const char* argn[], const char* argv[]) { 566 PLUGIN_PRINTF(("Plugin::Init (argc=%" NACL_PRIu32 ")\n", argc)); 567 HistogramEnumerateOsArch(GetSandboxISA()); 568 init_time_ = NaClGetTimeOfDayMicroseconds(); 569 570 ScriptablePlugin* scriptable_plugin = ScriptablePlugin::NewPlugin(this); 571 if (scriptable_plugin == NULL) 572 return false; 573 574 set_scriptable_plugin(scriptable_plugin); 575 PLUGIN_PRINTF(("Plugin::Init (scriptable_handle=%p)\n", 576 static_cast<void*>(scriptable_plugin_))); 577 url_util_ = pp::URLUtil_Dev::Get(); 578 if (url_util_ == NULL) 579 return false; 580 581 PLUGIN_PRINTF(("Plugin::Init (url_util_=%p)\n", 582 static_cast<const void*>(url_util_))); 583 584 bool status = EarlyInit(static_cast<int>(argc), argn, argv); 585 if (status) { 586 // Look for the developer attribute; if it's present, enable 'dev' 587 // interfaces. 588 const char* dev_settings = LookupArgument(kDevAttribute); 589 enable_dev_interfaces_ = (dev_settings != NULL); 590 591 const char* type_attr = LookupArgument(kTypeAttribute); 592 if (type_attr != NULL) { 593 mime_type_ = nacl::string(type_attr); 594 std::transform(mime_type_.begin(), mime_type_.end(), mime_type_.begin(), 595 tolower); 596 } 597 598 const char* manifest_url = LookupArgument(kSrcManifestAttribute); 599 if (NexeIsContentHandler()) { 600 // For content handlers 'src' will be the URL for the content 601 // and 'nacl' will be the URL for the manifest. 602 manifest_url = LookupArgument(kNaClManifestAttribute); 603 // For content handlers the NEXE runs in the security context of the 604 // content it is rendering and the NEXE itself appears to be a 605 // cross-origin resource stored in a Chrome extension. 606 } 607 // Use the document URL as the base for resolving relative URLs to find the 608 // manifest. This takes into account the setting of <base> tags that 609 // precede the embed/object. 610 CHECK(url_util_ != NULL); 611 pp::Var base_var = url_util_->GetDocumentURL(this); 612 if (!base_var.is_string()) { 613 PLUGIN_PRINTF(("Plugin::Init (unable to find document url)\n")); 614 return false; 615 } 616 set_plugin_base_url(base_var.AsString()); 617 if (manifest_url == NULL) { 618 // TODO(sehr,polina): this should be a hard error when scripting 619 // the src property is no longer allowed. 620 PLUGIN_PRINTF(("Plugin::Init:" 621 " WARNING: no 'src' property, so no manifest loaded.\n")); 622 if (NULL != LookupArgument(kNaClManifestAttribute)) { 623 PLUGIN_PRINTF(("Plugin::Init:" 624 " WARNING: 'nacl' property is incorrect. Use 'src'.\n")); 625 } 626 } else { 627 // Issue a GET for the manifest_url. The manifest file will be parsed to 628 // determine the nexe URL. 629 // Sets src property to full manifest URL. 630 RequestNaClManifest(manifest_url); 631 } 632 } 633 634 PLUGIN_PRINTF(("Plugin::Init (status=%d)\n", status)); 635 return status; 636 } 637 638 Plugin::Plugin(PP_Instance pp_instance) 639 : pp::InstancePrivate(pp_instance), 640 scriptable_plugin_(NULL), 641 argc_(-1), 642 argn_(NULL), 643 argv_(NULL), 644 main_subprocess_("main subprocess", NULL, NULL), 645 nexe_error_reported_(false), 646 wrapper_factory_(NULL), 647 enable_dev_interfaces_(false), 648 is_installed_(false), 649 init_time_(0), 650 ready_time_(0), 651 nexe_size_(0), 652 time_of_last_progress_event_(0), 653 exit_status_(-1), 654 nacl_interface_(NULL) { 655 PLUGIN_PRINTF(("Plugin::Plugin (this=%p, pp_instance=%" 656 NACL_PRId32 ")\n", static_cast<void*>(this), pp_instance)); 657 callback_factory_.Initialize(this); 658 nexe_downloader_.Initialize(this); 659 nacl_interface_ = GetNaClInterface(); 660 CHECK(nacl_interface_ != NULL); 661 set_nacl_ready_state(UNSENT); 662 set_last_error_string(""); 663 // We call set_exit_status() here to ensure that the 'exitStatus' property is 664 // set. This can only be called when nacl_interface_ is not NULL. 665 set_exit_status(-1); 666 } 667 668 669 Plugin::~Plugin() { 670 int64_t shutdown_start = NaClGetTimeOfDayMicroseconds(); 671 672 PLUGIN_PRINTF(("Plugin::~Plugin (this=%p, scriptable_plugin=%p)\n", 673 static_cast<void*>(this), 674 static_cast<void*>(scriptable_plugin()))); 675 // Destroy the coordinator while the rest of the data is still there 676 pnacl_coordinator_.reset(NULL); 677 678 if (!nexe_error_reported()) { 679 HistogramTimeLarge( 680 "NaCl.ModuleUptime.Normal", 681 (shutdown_start - ready_time_) / NACL_MICROS_PER_MILLI); 682 } 683 684 url_downloaders_.erase(url_downloaders_.begin(), url_downloaders_.end()); 685 686 ScriptablePlugin* scriptable_plugin_ = scriptable_plugin(); 687 ScriptablePlugin::Unref(&scriptable_plugin_); 688 689 // ShutDownSubprocesses shuts down the main subprocess, which shuts 690 // down the main ServiceRuntime object, which kills the subprocess. 691 // As a side effect of the subprocess being killed, the reverse 692 // services thread(s) will get EOF on the reverse channel(s), and 693 // the thread(s) will exit. In ServiceRuntime::Shutdown, we invoke 694 // ReverseService::WaitForServiceThreadsToExit(), so that there will 695 // not be an extent thread(s) hanging around. This means that the 696 // ~Plugin will block until this happens. This is a requirement, 697 // since the renderer should be free to unload the plugin code, and 698 // we cannot have threads running code that gets unloaded before 699 // they exit. 700 // 701 // By waiting for the threads here, we also ensure that the Plugin 702 // object and the subprocess and ServiceRuntime objects is not 703 // (fully) destroyed while the threads are running, so resources 704 // that are destroyed after ShutDownSubprocesses (below) are 705 // guaranteed to be live and valid for access from the service 706 // threads. 707 // 708 // The main_subprocess object, which wraps the main service_runtime 709 // object, is dtor'd implicitly after the explicit code below runs, 710 // so the main service runtime object will not have been dtor'd, 711 // though the Shutdown method may have been called, during the 712 // lifetime of the service threads. 713 ShutDownSubprocesses(); 714 715 delete wrapper_factory_; 716 delete[] argv_; 717 delete[] argn_; 718 719 HistogramTimeSmall( 720 "NaCl.Perf.ShutdownTime.Total", 721 (NaClGetTimeOfDayMicroseconds() - shutdown_start) 722 / NACL_MICROS_PER_MILLI); 723 724 PLUGIN_PRINTF(("Plugin::~Plugin (this=%p, return)\n", 725 static_cast<void*>(this))); 726 } 727 728 bool Plugin::HandleDocumentLoad(const pp::URLLoader& url_loader) { 729 PLUGIN_PRINTF(("Plugin::HandleDocumentLoad (this=%p)\n", 730 static_cast<void*>(this))); 731 // We don't know if the plugin will handle the document load, but return 732 // true in order to give it a chance to respond once the proxy is started. 733 return true; 734 } 735 736 pp::Var Plugin::GetInstanceObject() { 737 PLUGIN_PRINTF(("Plugin::GetInstanceObject (this=%p)\n", 738 static_cast<void*>(this))); 739 // The browser will unref when it discards the var for this object. 740 ScriptablePlugin* handle = 741 static_cast<ScriptablePlugin*>(scriptable_plugin()->AddRef()); 742 pp::Var* handle_var = handle->var(); 743 PLUGIN_PRINTF(("Plugin::GetInstanceObject (handle=%p, handle_var=%p)\n", 744 static_cast<void*>(handle), static_cast<void*>(handle_var))); 745 return *handle_var; // make a copy 746 } 747 748 void Plugin::HistogramStartupTimeSmall(const std::string& name, float dt) { 749 if (nexe_size_ > 0) { 750 float size_in_MB = static_cast<float>(nexe_size_) / (1024.f * 1024.f); 751 HistogramTimeSmall(name, static_cast<int64_t>(dt)); 752 HistogramTimeSmall(name + "PerMB", static_cast<int64_t>(dt / size_in_MB)); 753 } 754 } 755 756 void Plugin::HistogramStartupTimeMedium(const std::string& name, float dt) { 757 if (nexe_size_ > 0) { 758 float size_in_MB = static_cast<float>(nexe_size_) / (1024.f * 1024.f); 759 HistogramTimeMedium(name, static_cast<int64_t>(dt)); 760 HistogramTimeMedium(name + "PerMB", static_cast<int64_t>(dt / size_in_MB)); 761 } 762 } 763 764 void Plugin::NexeFileDidOpen(int32_t pp_error) { 765 PLUGIN_PRINTF(("Plugin::NexeFileDidOpen (pp_error=%" NACL_PRId32 ")\n", 766 pp_error)); 767 struct NaClFileInfo info = nexe_downloader_.GetFileInfo(); 768 PLUGIN_PRINTF(("Plugin::NexeFileDidOpen (file_desc=%" NACL_PRId32 ")\n", 769 info.desc)); 770 HistogramHTTPStatusCode( 771 is_installed_ ? 772 "NaCl.HttpStatusCodeClass.Nexe.InstalledApp" : 773 "NaCl.HttpStatusCodeClass.Nexe.NotInstalledApp", 774 nexe_downloader_.status_code()); 775 ErrorInfo error_info; 776 if (pp_error != PP_OK || info.desc == NACL_NO_FILE_DESC) { 777 if (pp_error == PP_ERROR_ABORTED) { 778 ReportLoadAbort(); 779 } else if (pp_error == PP_ERROR_NOACCESS) { 780 error_info.SetReport(ERROR_NEXE_NOACCESS_URL, 781 "access to nexe url was denied."); 782 ReportLoadError(error_info); 783 } else { 784 error_info.SetReport(ERROR_NEXE_LOAD_URL, "could not load nexe url."); 785 ReportLoadError(error_info); 786 } 787 return; 788 } 789 int32_t file_desc_ok_to_close = DUP(info.desc); 790 if (file_desc_ok_to_close == NACL_NO_FILE_DESC) { 791 error_info.SetReport(ERROR_NEXE_FH_DUP, 792 "could not duplicate loaded file handle."); 793 ReportLoadError(error_info); 794 return; 795 } 796 struct stat stat_buf; 797 if (0 != fstat(file_desc_ok_to_close, &stat_buf)) { 798 CLOSE(file_desc_ok_to_close); 799 error_info.SetReport(ERROR_NEXE_STAT, "could not stat nexe file."); 800 ReportLoadError(error_info); 801 return; 802 } 803 size_t nexe_bytes_read = static_cast<size_t>(stat_buf.st_size); 804 805 nexe_size_ = nexe_bytes_read; 806 HistogramSizeKB("NaCl.Perf.Size.Nexe", 807 static_cast<int32_t>(nexe_size_ / 1024)); 808 HistogramStartupTimeMedium( 809 "NaCl.Perf.StartupTime.NexeDownload", 810 static_cast<float>(nexe_downloader_.TimeSinceOpenMilliseconds())); 811 812 // Inform JavaScript that we successfully downloaded the nacl module. 813 EnqueueProgressEvent(PP_NACL_EVENT_PROGRESS, 814 nexe_downloader_.url_to_open(), 815 LENGTH_IS_COMPUTABLE, 816 nexe_bytes_read, 817 nexe_bytes_read); 818 819 load_start_ = NaClGetTimeOfDayMicroseconds(); 820 nacl::scoped_ptr<nacl::DescWrapper> 821 wrapper(wrapper_factory()->MakeFileDesc(file_desc_ok_to_close, O_RDONLY)); 822 NaClLog(4, "NexeFileDidOpen: invoking LoadNaClModule\n"); 823 bool was_successful = LoadNaClModule( 824 wrapper.get(), &error_info, 825 true, /* enable_dyncode_syscalls */ 826 true, /* enable_exception_handling */ 827 false, /* enable_crash_throttling */ 828 callback_factory_.NewCallback(&Plugin::NexeFileDidOpenContinuation), 829 callback_factory_.NewCallback(&Plugin::NexeDidCrash)); 830 831 if (!was_successful) { 832 ReportLoadError(error_info); 833 } 834 } 835 836 void Plugin::NexeFileDidOpenContinuation(int32_t pp_error) { 837 ErrorInfo error_info; 838 bool was_successful; 839 840 UNREFERENCED_PARAMETER(pp_error); 841 NaClLog(4, "Entered NexeFileDidOpenContinuation\n"); 842 NaClLog(4, "NexeFileDidOpenContinuation: invoking" 843 " LoadNaClModuleContinuationIntern\n"); 844 was_successful = LoadNaClModuleContinuationIntern(&error_info); 845 if (was_successful) { 846 NaClLog(4, "NexeFileDidOpenContinuation: success;" 847 " setting histograms\n"); 848 ready_time_ = NaClGetTimeOfDayMicroseconds(); 849 HistogramStartupTimeSmall( 850 "NaCl.Perf.StartupTime.LoadModule", 851 static_cast<float>(ready_time_ - load_start_) / NACL_MICROS_PER_MILLI); 852 HistogramStartupTimeMedium( 853 "NaCl.Perf.StartupTime.Total", 854 static_cast<float>(ready_time_ - init_time_) / NACL_MICROS_PER_MILLI); 855 856 ReportLoadSuccess(LENGTH_IS_COMPUTABLE, nexe_size_, nexe_size_); 857 } else { 858 NaClLog(4, "NexeFileDidOpenContinuation: failed."); 859 ReportLoadError(error_info); 860 } 861 NaClLog(4, "Leaving NexeFileDidOpenContinuation\n"); 862 } 863 864 static void LogLineToConsole(Plugin* plugin, const nacl::string& one_line) { 865 PLUGIN_PRINTF(("LogLineToConsole: %s\n", 866 one_line.c_str())); 867 plugin->AddToConsole(one_line); 868 } 869 870 void Plugin::CopyCrashLogToJsConsole() { 871 nacl::string fatal_msg(main_service_runtime()->GetCrashLogOutput()); 872 size_t ix_start = 0; 873 size_t ix_end; 874 875 PLUGIN_PRINTF(("Plugin::CopyCrashLogToJsConsole: got %" NACL_PRIuS " bytes\n", 876 fatal_msg.size())); 877 while (nacl::string::npos != (ix_end = fatal_msg.find('\n', ix_start))) { 878 LogLineToConsole(this, fatal_msg.substr(ix_start, ix_end - ix_start)); 879 ix_start = ix_end + 1; 880 } 881 if (ix_start != fatal_msg.size()) { 882 LogLineToConsole(this, fatal_msg.substr(ix_start)); 883 } 884 } 885 886 void Plugin::NexeDidCrash(int32_t pp_error) { 887 PLUGIN_PRINTF(("Plugin::NexeDidCrash (pp_error=%" NACL_PRId32 ")\n", 888 pp_error)); 889 if (pp_error != PP_OK) { 890 PLUGIN_PRINTF(("Plugin::NexeDidCrash: CallOnMainThread callback with" 891 " non-PP_OK arg -- SHOULD NOT HAPPEN\n")); 892 } 893 PLUGIN_PRINTF(("Plugin::NexeDidCrash: crash event!\n")); 894 if (-1 != exit_status()) { 895 // The NaCl module voluntarily exited. However, this is still a 896 // crash from the point of view of Pepper, since PPAPI plugins are 897 // event handlers and should never exit. 898 PLUGIN_PRINTF((("Plugin::NexeDidCrash: nexe exited with status %d" 899 " so this is a \"controlled crash\".\n"), 900 exit_status())); 901 } 902 // If the crash occurs during load, we just want to report an error 903 // that fits into our load progress event grammar. If the crash 904 // occurs after loaded/loadend, then we use ReportDeadNexe to send a 905 // "crash" event. 906 if (nexe_error_reported()) { 907 PLUGIN_PRINTF(("Plugin::NexeDidCrash: error already reported;" 908 " suppressing\n")); 909 } else { 910 if (nacl_ready_state_ == DONE) { 911 ReportDeadNexe(); 912 } else { 913 ErrorInfo error_info; 914 // The error is not quite right. In particular, the crash 915 // reported by this path could be due to NaCl application 916 // crashes that occur after the PPAPI proxy has started. 917 error_info.SetReport(ERROR_START_PROXY_CRASH, 918 "Nexe crashed during startup"); 919 ReportLoadError(error_info); 920 } 921 } 922 923 // In all cases, try to grab the crash log. The first error 924 // reported may have come from the start_module RPC reply indicating 925 // a validation error or something similar, which wouldn't grab the 926 // crash log. In the event that this is called twice, the second 927 // invocation will just be a no-op, since all the crash log will 928 // have been received and we'll just get an EOF indication. 929 CopyCrashLogToJsConsole(); 930 } 931 932 void Plugin::BitcodeDidTranslate(int32_t pp_error) { 933 PLUGIN_PRINTF(("Plugin::BitcodeDidTranslate (pp_error=%" NACL_PRId32 ")\n", 934 pp_error)); 935 if (pp_error != PP_OK) { 936 // Error should have been reported by pnacl. Just return. 937 PLUGIN_PRINTF(("Plugin::BitcodeDidTranslate error in Pnacl\n")); 938 return; 939 } 940 941 // Inform JavaScript that we successfully translated the bitcode to a nexe. 942 nacl::scoped_ptr<nacl::DescWrapper> 943 wrapper(pnacl_coordinator_.get()->ReleaseTranslatedFD()); 944 ErrorInfo error_info; 945 bool was_successful = LoadNaClModule( 946 wrapper.get(), &error_info, 947 false, /* enable_dyncode_syscalls */ 948 false, /* enable_exception_handling */ 949 true, /* enable_crash_throttling */ 950 callback_factory_.NewCallback(&Plugin::BitcodeDidTranslateContinuation), 951 callback_factory_.NewCallback(&Plugin::NexeDidCrash)); 952 953 if (!was_successful) { 954 ReportLoadError(error_info); 955 } 956 } 957 958 void Plugin::BitcodeDidTranslateContinuation(int32_t pp_error) { 959 ErrorInfo error_info; 960 bool was_successful = LoadNaClModuleContinuationIntern(&error_info); 961 962 NaClLog(4, "Entered BitcodeDidTranslateContinuation\n"); 963 UNREFERENCED_PARAMETER(pp_error); 964 if (was_successful) { 965 int64_t loaded; 966 int64_t total; 967 pnacl_coordinator_->GetCurrentProgress(&loaded, &total); 968 ReportLoadSuccess(LENGTH_IS_COMPUTABLE, loaded, total); 969 } else { 970 ReportLoadError(error_info); 971 } 972 } 973 974 void Plugin::ReportDeadNexe() { 975 PLUGIN_PRINTF(("Plugin::ReportDeadNexe\n")); 976 977 if (nacl_ready_state_ == DONE && !nexe_error_reported()) { // After loadEnd. 978 int64_t crash_time = NaClGetTimeOfDayMicroseconds(); 979 // Crashes will be more likely near startup, so use a medium histogram 980 // instead of a large one. 981 HistogramTimeMedium( 982 "NaCl.ModuleUptime.Crash", 983 (crash_time - ready_time_) / NACL_MICROS_PER_MILLI); 984 985 nacl::string message = nacl::string("NaCl module crashed"); 986 set_last_error_string(message); 987 AddToConsole(message); 988 989 EnqueueProgressEvent(PP_NACL_EVENT_CRASH); 990 set_nexe_error_reported(true); 991 } 992 // else ReportLoadError() and ReportAbortError() will be used by loading code 993 // to provide error handling. 994 // 995 // NOTE: not all crashes during load will make it here. 996 // Those in BrowserPpp::InitializeModule and creation of PPP interfaces 997 // will just get reported back as PP_ERROR_FAILED. 998 } 999 1000 void Plugin::NaClManifestBufferReady(int32_t pp_error) { 1001 PLUGIN_PRINTF(("Plugin::NaClManifestBufferReady (pp_error=%" 1002 NACL_PRId32 ")\n", pp_error)); 1003 ErrorInfo error_info; 1004 set_manifest_url(nexe_downloader_.url()); 1005 if (pp_error != PP_OK) { 1006 if (pp_error == PP_ERROR_ABORTED) { 1007 ReportLoadAbort(); 1008 } else { 1009 error_info.SetReport(ERROR_MANIFEST_LOAD_URL, 1010 "could not load manifest url."); 1011 ReportLoadError(error_info); 1012 } 1013 return; 1014 } 1015 1016 const std::deque<char>& buffer = nexe_downloader_.buffer(); 1017 size_t buffer_size = buffer.size(); 1018 if (buffer_size > kNaClManifestMaxFileBytes) { 1019 error_info.SetReport(ERROR_MANIFEST_TOO_LARGE, 1020 "manifest file too large."); 1021 ReportLoadError(error_info); 1022 return; 1023 } 1024 nacl::scoped_array<char> json_buffer(new char[buffer_size + 1]); 1025 if (json_buffer == NULL) { 1026 error_info.SetReport(ERROR_MANIFEST_MEMORY_ALLOC, 1027 "could not allocate manifest memory."); 1028 ReportLoadError(error_info); 1029 return; 1030 } 1031 std::copy(buffer.begin(), buffer.begin() + buffer_size, &json_buffer[0]); 1032 json_buffer[buffer_size] = '\0'; 1033 1034 ProcessNaClManifest(json_buffer.get()); 1035 } 1036 1037 void Plugin::NaClManifestFileDidOpen(int32_t pp_error) { 1038 PLUGIN_PRINTF(("Plugin::NaClManifestFileDidOpen (pp_error=%" 1039 NACL_PRId32 ")\n", pp_error)); 1040 HistogramTimeSmall("NaCl.Perf.StartupTime.ManifestDownload", 1041 nexe_downloader_.TimeSinceOpenMilliseconds()); 1042 HistogramHTTPStatusCode( 1043 is_installed_ ? 1044 "NaCl.HttpStatusCodeClass.Manifest.InstalledApp" : 1045 "NaCl.HttpStatusCodeClass.Manifest.NotInstalledApp", 1046 nexe_downloader_.status_code()); 1047 ErrorInfo error_info; 1048 // The manifest file was successfully opened. Set the src property on the 1049 // plugin now, so that the full url is available to error handlers. 1050 set_manifest_url(nexe_downloader_.url()); 1051 struct NaClFileInfo info = nexe_downloader_.GetFileInfo(); 1052 PLUGIN_PRINTF(("Plugin::NaClManifestFileDidOpen (file_desc=%" 1053 NACL_PRId32 ")\n", info.desc)); 1054 if (pp_error != PP_OK || info.desc == NACL_NO_FILE_DESC) { 1055 if (pp_error == PP_ERROR_ABORTED) { 1056 ReportLoadAbort(); 1057 } else if (pp_error == PP_ERROR_NOACCESS) { 1058 error_info.SetReport(ERROR_MANIFEST_NOACCESS_URL, 1059 "access to manifest url was denied."); 1060 ReportLoadError(error_info); 1061 } else { 1062 error_info.SetReport(ERROR_MANIFEST_LOAD_URL, 1063 "could not load manifest url."); 1064 ReportLoadError(error_info); 1065 } 1066 return; 1067 } 1068 // SlurpFile closes the file descriptor after reading (or on error). 1069 // Duplicate our file descriptor since it will be handled by the browser. 1070 int dup_file_desc = DUP(info.desc); 1071 nacl::string json_buffer; 1072 file_utils::StatusCode status = file_utils::SlurpFile( 1073 dup_file_desc, json_buffer, kNaClManifestMaxFileBytes); 1074 1075 if (status != file_utils::PLUGIN_FILE_SUCCESS) { 1076 switch (status) { 1077 case file_utils::PLUGIN_FILE_SUCCESS: 1078 CHECK(0); 1079 break; 1080 case file_utils::PLUGIN_FILE_ERROR_MEM_ALLOC: 1081 error_info.SetReport(ERROR_MANIFEST_MEMORY_ALLOC, 1082 "could not allocate manifest memory."); 1083 break; 1084 case file_utils::PLUGIN_FILE_ERROR_OPEN: 1085 error_info.SetReport(ERROR_MANIFEST_OPEN, 1086 "could not open manifest file."); 1087 break; 1088 case file_utils::PLUGIN_FILE_ERROR_FILE_TOO_LARGE: 1089 error_info.SetReport(ERROR_MANIFEST_TOO_LARGE, 1090 "manifest file too large."); 1091 break; 1092 case file_utils::PLUGIN_FILE_ERROR_STAT: 1093 error_info.SetReport(ERROR_MANIFEST_STAT, 1094 "could not stat manifest file."); 1095 break; 1096 case file_utils::PLUGIN_FILE_ERROR_READ: 1097 error_info.SetReport(ERROR_MANIFEST_READ, 1098 "could not read manifest file."); 1099 break; 1100 } 1101 ReportLoadError(error_info); 1102 return; 1103 } 1104 1105 ProcessNaClManifest(json_buffer); 1106 } 1107 1108 void Plugin::ProcessNaClManifest(const nacl::string& manifest_json) { 1109 HistogramSizeKB("NaCl.Perf.Size.Manifest", 1110 static_cast<int32_t>(manifest_json.length() / 1024)); 1111 nacl::string program_url; 1112 PnaclOptions pnacl_options; 1113 ErrorInfo error_info; 1114 if (!SetManifestObject(manifest_json, &error_info)) { 1115 ReportLoadError(error_info); 1116 return; 1117 } 1118 1119 if (manifest_->GetProgramURL(&program_url, &pnacl_options, &error_info)) { 1120 is_installed_ = GetUrlScheme(program_url) == SCHEME_CHROME_EXTENSION; 1121 set_nacl_ready_state(LOADING); 1122 // Inform JavaScript that we found a nexe URL to load. 1123 EnqueueProgressEvent(PP_NACL_EVENT_PROGRESS); 1124 if (pnacl_options.translate()) { 1125 pp::CompletionCallback translate_callback = 1126 callback_factory_.NewCallback(&Plugin::BitcodeDidTranslate); 1127 // Will always call the callback on success or failure. 1128 pnacl_coordinator_.reset( 1129 PnaclCoordinator::BitcodeToNative(this, 1130 program_url, 1131 pnacl_options, 1132 translate_callback)); 1133 return; 1134 } else { 1135 // Try the fast path first. This will only block if the file is installed. 1136 if (OpenURLFast(program_url, &nexe_downloader_)) { 1137 NexeFileDidOpen(PP_OK); 1138 } else { 1139 pp::CompletionCallback open_callback = 1140 callback_factory_.NewCallback(&Plugin::NexeFileDidOpen); 1141 // Will always call the callback on success or failure. 1142 CHECK( 1143 nexe_downloader_.Open(program_url, 1144 DOWNLOAD_TO_FILE, 1145 open_callback, 1146 true, 1147 &UpdateDownloadProgress)); 1148 } 1149 return; 1150 } 1151 } 1152 // Failed to select the program and/or the translator. 1153 ReportLoadError(error_info); 1154 } 1155 1156 void Plugin::RequestNaClManifest(const nacl::string& url) { 1157 PLUGIN_PRINTF(("Plugin::RequestNaClManifest (url='%s')\n", url.c_str())); 1158 PLUGIN_PRINTF(("Plugin::RequestNaClManifest (plugin base url='%s')\n", 1159 plugin_base_url().c_str())); 1160 // The full URL of the manifest file is relative to the base url. 1161 CHECK(url_util_ != NULL); 1162 pp::Var nmf_resolved_url = 1163 url_util_->ResolveRelativeToURL(plugin_base_url(), pp::Var(url)); 1164 if (!nmf_resolved_url.is_string()) { 1165 ErrorInfo error_info; 1166 error_info.SetReport( 1167 ERROR_MANIFEST_RESOLVE_URL, 1168 nacl::string("could not resolve URL \"") + url.c_str() + 1169 "\" relative to \"" + plugin_base_url().c_str() + "\"."); 1170 ReportLoadError(error_info); 1171 return; 1172 } 1173 PLUGIN_PRINTF(("Plugin::RequestNaClManifest (resolved url='%s')\n", 1174 nmf_resolved_url.AsString().c_str())); 1175 is_installed_ = GetUrlScheme(nmf_resolved_url.AsString()) == 1176 SCHEME_CHROME_EXTENSION; 1177 set_manifest_base_url(nmf_resolved_url.AsString()); 1178 set_manifest_url(url); 1179 // Inform JavaScript that a load is starting. 1180 set_nacl_ready_state(OPENED); 1181 EnqueueProgressEvent(PP_NACL_EVENT_LOADSTART); 1182 bool is_data_uri = GetUrlScheme(nmf_resolved_url.AsString()) == SCHEME_DATA; 1183 HistogramEnumerateManifestIsDataURI(static_cast<int>(is_data_uri)); 1184 if (is_data_uri) { 1185 pp::CompletionCallback open_callback = 1186 callback_factory_.NewCallback(&Plugin::NaClManifestBufferReady); 1187 // Will always call the callback on success or failure. 1188 CHECK(nexe_downloader_.Open(nmf_resolved_url.AsString(), 1189 DOWNLOAD_TO_BUFFER, 1190 open_callback, 1191 false, 1192 NULL)); 1193 } else { 1194 pp::CompletionCallback open_callback = 1195 callback_factory_.NewCallback(&Plugin::NaClManifestFileDidOpen); 1196 // Will always call the callback on success or failure. 1197 CHECK(nexe_downloader_.Open(nmf_resolved_url.AsString(), 1198 DOWNLOAD_TO_FILE, 1199 open_callback, 1200 false, 1201 NULL)); 1202 } 1203 } 1204 1205 1206 bool Plugin::SetManifestObject(const nacl::string& manifest_json, 1207 ErrorInfo* error_info) { 1208 PLUGIN_PRINTF(("Plugin::SetManifestObject(): manifest_json='%s'.\n", 1209 manifest_json.c_str())); 1210 if (error_info == NULL) 1211 return false; 1212 // Determine whether lookups should use portable (i.e., pnacl versions) 1213 // rather than platform-specific files. 1214 bool is_pnacl = (mime_type() == kPnaclMIMEType); 1215 nacl::scoped_ptr<JsonManifest> json_manifest( 1216 new JsonManifest(url_util_, 1217 manifest_base_url(), 1218 (is_pnacl ? kPortableISA : GetSandboxISA()))); 1219 if (!json_manifest->Init(manifest_json, error_info)) { 1220 return false; 1221 } 1222 manifest_.reset(json_manifest.release()); 1223 return true; 1224 } 1225 1226 void Plugin::UrlDidOpenForStreamAsFile(int32_t pp_error, 1227 FileDownloader*& url_downloader, 1228 PP_CompletionCallback callback) { 1229 PLUGIN_PRINTF(("Plugin::UrlDidOpen (pp_error=%" NACL_PRId32 1230 ", url_downloader=%p)\n", pp_error, 1231 static_cast<void*>(url_downloader))); 1232 url_downloaders_.erase(url_downloader); 1233 nacl::scoped_ptr<FileDownloader> scoped_url_downloader(url_downloader); 1234 struct NaClFileInfo info = scoped_url_downloader->GetFileInfo(); 1235 1236 if (pp_error != PP_OK) { 1237 PP_RunCompletionCallback(&callback, pp_error); 1238 } else if (info.desc > NACL_NO_FILE_DESC) { 1239 url_file_info_map_[url_downloader->url_to_open()] = info; 1240 PP_RunCompletionCallback(&callback, PP_OK); 1241 } else { 1242 PP_RunCompletionCallback(&callback, PP_ERROR_FAILED); 1243 } 1244 } 1245 1246 struct NaClFileInfo Plugin::GetFileInfo(const nacl::string& url) { 1247 struct NaClFileInfo info; 1248 memset(&info, 0, sizeof(info)); 1249 std::map<nacl::string, struct NaClFileInfo>::iterator it = 1250 url_file_info_map_.find(url); 1251 if (it != url_file_info_map_.end()) { 1252 info = it->second; 1253 info.desc = DUP(info.desc); 1254 } else { 1255 info.desc = -1; 1256 } 1257 return info; 1258 } 1259 1260 bool Plugin::StreamAsFile(const nacl::string& url, 1261 PP_CompletionCallback callback) { 1262 PLUGIN_PRINTF(("Plugin::StreamAsFile (url='%s')\n", url.c_str())); 1263 FileDownloader* downloader = new FileDownloader(); 1264 downloader->Initialize(this); 1265 url_downloaders_.insert(downloader); 1266 // Untrusted loads are always relative to the page's origin. 1267 CHECK(url_util_ != NULL); 1268 pp::Var resolved_url = 1269 url_util_->ResolveRelativeToURL(pp::Var(plugin_base_url()), url); 1270 if (!resolved_url.is_string()) { 1271 PLUGIN_PRINTF(("Plugin::StreamAsFile: " 1272 "could not resolve url \"%s\" relative to plugin \"%s\".", 1273 url.c_str(), 1274 plugin_base_url().c_str())); 1275 return false; 1276 } 1277 1278 // Try the fast path first. This will only block if the file is installed. 1279 if (OpenURLFast(url, downloader)) { 1280 UrlDidOpenForStreamAsFile(PP_OK, downloader, callback); 1281 return true; 1282 } 1283 1284 pp::CompletionCallback open_callback = callback_factory_.NewCallback( 1285 &Plugin::UrlDidOpenForStreamAsFile, downloader, callback); 1286 // If true, will always call the callback on success or failure. 1287 return downloader->Open(url, 1288 DOWNLOAD_TO_FILE, 1289 open_callback, 1290 true, 1291 &UpdateDownloadProgress); 1292 } 1293 1294 1295 void Plugin::ReportLoadSuccess(LengthComputable length_computable, 1296 uint64_t loaded_bytes, 1297 uint64_t total_bytes) { 1298 // Set the readyState attribute to indicate loaded. 1299 set_nacl_ready_state(DONE); 1300 // Inform JavaScript that loading was successful and is complete. 1301 const nacl::string& url = nexe_downloader_.url_to_open(); 1302 EnqueueProgressEvent( 1303 PP_NACL_EVENT_LOAD, url, length_computable, loaded_bytes, total_bytes); 1304 EnqueueProgressEvent( 1305 PP_NACL_EVENT_LOADEND, url, length_computable, loaded_bytes, total_bytes); 1306 1307 // UMA 1308 HistogramEnumerateLoadStatus(ERROR_LOAD_SUCCESS, is_installed_); 1309 } 1310 1311 1312 // TODO(ncbray): report UMA stats 1313 void Plugin::ReportLoadError(const ErrorInfo& error_info) { 1314 PLUGIN_PRINTF(("Plugin::ReportLoadError (error='%s')\n", 1315 error_info.message().c_str())); 1316 // For errors the user (and not just the developer) should know about, 1317 // report them to the renderer so the browser can display a message. 1318 if (error_info.error_code() == ERROR_MANIFEST_PROGRAM_MISSING_ARCH) { 1319 // A special case: the manifest may otherwise be valid but is missing 1320 // a program/file compatible with the user's sandbox. 1321 nacl_interface()->ReportNaClError(pp_instance(), 1322 PP_NACL_MANIFEST_MISSING_ARCH); 1323 } 1324 1325 // Set the readyState attribute to indicate we need to start over. 1326 set_nacl_ready_state(DONE); 1327 set_nexe_error_reported(true); 1328 // Report an error in lastError and on the JavaScript console. 1329 nacl::string message = nacl::string("NaCl module load failed: ") + 1330 error_info.message(); 1331 set_last_error_string(message); 1332 AddToConsole(nacl::string("NaCl module load failed: ") + 1333 error_info.console_message()); 1334 // Inform JavaScript that loading encountered an error and is complete. 1335 EnqueueProgressEvent(PP_NACL_EVENT_ERROR); 1336 EnqueueProgressEvent(PP_NACL_EVENT_LOADEND); 1337 1338 // UMA 1339 HistogramEnumerateLoadStatus(error_info.error_code(), is_installed_); 1340 } 1341 1342 1343 void Plugin::ReportLoadAbort() { 1344 PLUGIN_PRINTF(("Plugin::ReportLoadAbort\n")); 1345 // Set the readyState attribute to indicate we need to start over. 1346 set_nacl_ready_state(DONE); 1347 set_nexe_error_reported(true); 1348 // Report an error in lastError and on the JavaScript console. 1349 nacl::string error_string("NaCl module load failed: user aborted"); 1350 set_last_error_string(error_string); 1351 AddToConsole(error_string); 1352 // Inform JavaScript that loading was aborted and is complete. 1353 EnqueueProgressEvent(PP_NACL_EVENT_ABORT); 1354 EnqueueProgressEvent(PP_NACL_EVENT_LOADEND); 1355 1356 // UMA 1357 HistogramEnumerateLoadStatus(ERROR_LOAD_ABORTED, is_installed_); 1358 } 1359 1360 void Plugin::UpdateDownloadProgress( 1361 PP_Instance pp_instance, 1362 PP_Resource pp_resource, 1363 int64_t /*bytes_sent*/, 1364 int64_t /*total_bytes_to_be_sent*/, 1365 int64_t bytes_received, 1366 int64_t total_bytes_to_be_received) { 1367 Instance* instance = pp::Module::Get()->InstanceForPPInstance(pp_instance); 1368 if (instance != NULL) { 1369 Plugin* plugin = static_cast<Plugin*>(instance); 1370 // Rate limit progress events to a maximum of 100 per second. 1371 int64_t time = NaClGetTimeOfDayMicroseconds(); 1372 int64_t elapsed = time - plugin->time_of_last_progress_event_; 1373 const int64_t kTenMilliseconds = 10000; 1374 if (elapsed > kTenMilliseconds) { 1375 plugin->time_of_last_progress_event_ = time; 1376 1377 // Find the URL loader that sent this notification. 1378 const FileDownloader* file_downloader = 1379 plugin->FindFileDownloader(pp_resource); 1380 // If not a streamed file, it must be the .nexe loader. 1381 if (file_downloader == NULL) 1382 file_downloader = &plugin->nexe_downloader_; 1383 nacl::string url = file_downloader->url_to_open(); 1384 LengthComputable length_computable = (total_bytes_to_be_received >= 0) ? 1385 LENGTH_IS_COMPUTABLE : LENGTH_IS_NOT_COMPUTABLE; 1386 1387 plugin->EnqueueProgressEvent(PP_NACL_EVENT_PROGRESS, 1388 url, 1389 length_computable, 1390 bytes_received, 1391 total_bytes_to_be_received); 1392 } 1393 } 1394 } 1395 1396 const FileDownloader* Plugin::FindFileDownloader( 1397 PP_Resource url_loader) const { 1398 const FileDownloader* file_downloader = NULL; 1399 if (url_loader == nexe_downloader_.url_loader()) { 1400 file_downloader = &nexe_downloader_; 1401 } else { 1402 std::set<FileDownloader*>::const_iterator it = url_downloaders_.begin(); 1403 while (it != url_downloaders_.end()) { 1404 if (url_loader == (*it)->url_loader()) { 1405 file_downloader = (*it); 1406 break; 1407 } 1408 ++it; 1409 } 1410 } 1411 return file_downloader; 1412 } 1413 1414 void Plugin::EnqueueProgressEvent(PP_NaClEventType event_type) { 1415 EnqueueProgressEvent(event_type, 1416 NACL_NO_URL, 1417 Plugin::LENGTH_IS_NOT_COMPUTABLE, 1418 Plugin::kUnknownBytes, 1419 Plugin::kUnknownBytes); 1420 } 1421 1422 void Plugin::EnqueueProgressEvent(PP_NaClEventType event_type, 1423 const nacl::string& url, 1424 LengthComputable length_computable, 1425 uint64_t loaded_bytes, 1426 uint64_t total_bytes) { 1427 PLUGIN_PRINTF(("Plugin::EnqueueProgressEvent (" 1428 "event_type='%d', url='%s', length_computable=%d, " 1429 "loaded=%" NACL_PRIu64 ", total=%" NACL_PRIu64 ")\n", 1430 static_cast<int>(event_type), 1431 url.c_str(), 1432 static_cast<int>(length_computable), 1433 loaded_bytes, 1434 total_bytes)); 1435 1436 progress_events_.push(new ProgressEvent(event_type, 1437 url, 1438 length_computable, 1439 loaded_bytes, 1440 total_bytes)); 1441 // Note that using callback_factory_ in this way is not thread safe. 1442 // If/when EnqueueProgressEvent is callable from another thread, this 1443 // will need to change. 1444 pp::CompletionCallback callback = 1445 callback_factory_.NewCallback(&Plugin::DispatchProgressEvent); 1446 pp::Core* core = pp::Module::Get()->core(); 1447 core->CallOnMainThread(0, callback, 0); 1448 } 1449 1450 void Plugin::ReportSelLdrLoadStatus(int status) { 1451 HistogramEnumerateSelLdrLoadStatus(static_cast<NaClErrorCode>(status), 1452 is_installed_); 1453 } 1454 1455 void Plugin::DispatchProgressEvent(int32_t result) { 1456 PLUGIN_PRINTF(("Plugin::DispatchProgressEvent (result=%" 1457 NACL_PRId32 ")\n", result)); 1458 if (result < 0) { 1459 return; 1460 } 1461 if (progress_events_.empty()) { 1462 PLUGIN_PRINTF(("Plugin::DispatchProgressEvent: no pending events\n")); 1463 return; 1464 } 1465 nacl::scoped_ptr<ProgressEvent> event(progress_events_.front()); 1466 progress_events_.pop(); 1467 PLUGIN_PRINTF(("Plugin::DispatchProgressEvent (" 1468 "event_type='%d', url='%s', length_computable=%d, " 1469 "loaded=%" NACL_PRIu64 ", total=%" NACL_PRIu64 ")\n", 1470 static_cast<int>(event->event_type()), 1471 event->url(), 1472 static_cast<int>(event->length_computable()), 1473 event->loaded_bytes(), 1474 event->total_bytes())); 1475 1476 nacl_interface_->DispatchEvent( 1477 pp_instance(), 1478 event->event_type(), 1479 pp::Var(event->url()).pp_var(), 1480 event->length_computable() == LENGTH_IS_COMPUTABLE ? PP_TRUE : PP_FALSE, 1481 event->loaded_bytes(), 1482 event->total_bytes()); 1483 } 1484 1485 bool Plugin::OpenURLFast(const nacl::string& url, 1486 FileDownloader* downloader) { 1487 // Fast path only works for installed file URLs. 1488 if (GetUrlScheme(url) != SCHEME_CHROME_EXTENSION) 1489 return false; 1490 // IMPORTANT: Make sure the document can request the given URL. If we don't 1491 // check, a malicious app could probe the extension system. This enforces a 1492 // same-origin policy which prevents the app from requesting resources from 1493 // another app. 1494 if (!DocumentCanRequest(url)) 1495 return false; 1496 1497 uint64_t file_token_lo = 0; 1498 uint64_t file_token_hi = 0; 1499 PP_FileHandle file_handle = 1500 nacl_interface()->OpenNaClExecutable(pp_instance(), 1501 url.c_str(), 1502 &file_token_lo, &file_token_hi); 1503 // We shouldn't hit this if the file URL is in an installed app. 1504 if (file_handle == PP_kInvalidFileHandle) 1505 return false; 1506 1507 // FileDownloader takes ownership of the file handle. 1508 downloader->OpenFast(url, file_handle, file_token_lo, file_token_hi); 1509 return true; 1510 } 1511 1512 UrlSchemeType Plugin::GetUrlScheme(const std::string& url) { 1513 CHECK(url_util_ != NULL); 1514 PP_URLComponents_Dev comps; 1515 pp::Var canonicalized = 1516 url_util_->Canonicalize(pp::Var(url), &comps); 1517 1518 if (canonicalized.is_null() || 1519 (comps.scheme.begin == 0 && comps.scheme.len == -1)) { 1520 // |url| was an invalid URL or has no scheme. 1521 return SCHEME_OTHER; 1522 } 1523 1524 CHECK(comps.scheme.begin < 1525 static_cast<int>(canonicalized.AsString().size())); 1526 CHECK(comps.scheme.begin + comps.scheme.len < 1527 static_cast<int>(canonicalized.AsString().size())); 1528 1529 std::string scheme = canonicalized.AsString().substr(comps.scheme.begin, 1530 comps.scheme.len); 1531 if (scheme == kChromeExtensionUriScheme) 1532 return SCHEME_CHROME_EXTENSION; 1533 if (scheme == kDataUriScheme) 1534 return SCHEME_DATA; 1535 return SCHEME_OTHER; 1536 } 1537 1538 bool Plugin::DocumentCanRequest(const std::string& url) { 1539 CHECK(url_util_ != NULL); 1540 return url_util_->DocumentCanRequest(this, pp::Var(url)); 1541 } 1542 1543 void Plugin::AddToConsole(const nacl::string& text) { 1544 pp::Module* module = pp::Module::Get(); 1545 const PPB_Var* var_interface = 1546 static_cast<const PPB_Var*>( 1547 module->GetBrowserInterface(PPB_VAR_INTERFACE)); 1548 nacl::string prefix_string("NativeClient"); 1549 PP_Var prefix = 1550 var_interface->VarFromUtf8(prefix_string.c_str(), 1551 static_cast<uint32_t>(prefix_string.size())); 1552 PP_Var str = var_interface->VarFromUtf8(text.c_str(), 1553 static_cast<uint32_t>(text.size())); 1554 const PPB_Console* console_interface = 1555 static_cast<const PPB_Console*>( 1556 module->GetBrowserInterface(PPB_CONSOLE_INTERFACE)); 1557 console_interface->LogWithSource(pp_instance(), PP_LOGLEVEL_LOG, prefix, str); 1558 var_interface->Release(prefix); 1559 var_interface->Release(str); 1560 } 1561 1562 void Plugin::set_last_error_string(const nacl::string& error) { 1563 DCHECK(nacl_interface_); 1564 nacl_interface_->SetReadOnlyProperty(pp_instance(), 1565 pp::Var("lastError").pp_var(), 1566 pp::Var(error).pp_var()); 1567 } 1568 1569 void Plugin::set_nacl_ready_state(ReadyState state) { 1570 nacl_ready_state_ = state; 1571 DCHECK(nacl_interface_); 1572 nacl_interface_->SetReadOnlyProperty(pp_instance(), 1573 pp::Var("readyState").pp_var(), 1574 pp::Var(state).pp_var()); 1575 } 1576 1577 void Plugin::set_exit_status(int exit_status) { 1578 pp::Core* core = pp::Module::Get()->core(); 1579 if (core->IsMainThread()) { 1580 SetExitStatusOnMainThread(PP_OK, exit_status); 1581 } else { 1582 pp::CompletionCallback callback = 1583 callback_factory_.NewCallback(&Plugin::SetExitStatusOnMainThread, 1584 exit_status); 1585 core->CallOnMainThread(0, callback, 0); 1586 } 1587 } 1588 1589 void Plugin::SetExitStatusOnMainThread(int32_t pp_error, 1590 int exit_status) { 1591 DCHECK(pp::Module::Get()->core()->IsMainThread()); 1592 DCHECK(nacl_interface_); 1593 exit_status_ = exit_status; 1594 nacl_interface_->SetReadOnlyProperty(pp_instance(), 1595 pp::Var("exitStatus").pp_var(), 1596 pp::Var(exit_status_).pp_var()); 1597 } 1598 1599 1600 } // namespace plugin 1601