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