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.
6 // Do not warn about use of std::copy with raw pointers.
7 #pragma warning(disable : 4996)
10 #include "ppapi/native_client/src/trusted/plugin/plugin.h"
13 #include <sys/types.h>
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"
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/cpp/dev/url_util_dev.h"
38 #include "ppapi/cpp/module.h"
39 #include "ppapi/cpp/text_input_controller.h"
41 #include "ppapi/native_client/src/trusted/plugin/file_utils.h"
42 #include "ppapi/native_client/src/trusted/plugin/json_manifest.h"
43 #include "ppapi/native_client/src/trusted/plugin/nacl_entry_points.h"
44 #include "ppapi/native_client/src/trusted/plugin/nacl_subprocess.h"
45 #include "ppapi/native_client/src/trusted/plugin/nexe_arch.h"
46 #include "ppapi/native_client/src/trusted/plugin/plugin_error.h"
47 #include "ppapi/native_client/src/trusted/plugin/scriptable_plugin.h"
48 #include "ppapi/native_client/src/trusted/plugin/service_runtime.h"
49 #include "ppapi/native_client/src/trusted/plugin/utility.h"
55 const char* const kTypeAttribute = "type";
56 // The "src" attribute of the <embed> tag. The value is expected to be either
57 // a URL or URI pointing to the manifest file (which is expected to contain
58 // JSON matching ISAs with .nexe URLs).
59 const char* const kSrcManifestAttribute = "src";
60 // The "nacl" attribute of the <embed> tag. We use the value of this attribute
61 // to find the manifest file when NaCl is registered as a plug-in for another
62 // MIME type because the "src" attribute is used to supply us with the resource
63 // of that MIME type that we're supposed to display.
64 const char* const kNaClManifestAttribute = "nacl";
65 // The pseudo-ISA used to indicate portable native client.
66 const char* const kPortableISA = "portable";
67 // This is a pretty arbitrary limit on the byte size of the NaCl manfest file.
68 // Note that the resulting string object has to have at least one byte extra
69 // for the null termination character.
70 const size_t kNaClManifestMaxFileBytes = 1024 * 1024;
72 // Define an argument name to enable 'dev' interfaces. To make sure it doesn't
73 // collide with any user-defined HTML attribute, make the first character '@'.
74 const char* const kDevAttribute = "@dev";
76 // URL schemes that we treat in special ways.
77 const char* const kChromeExtensionUriScheme = "chrome-extension";
78 const char* const kDataUriScheme = "data";
81 const int64_t kTimeSmallMin = 1; // in ms
82 const int64_t kTimeSmallMax = 20000; // in ms
83 const uint32_t kTimeSmallBuckets = 100;
85 // Up to 3 minutes, 20 seconds
86 const int64_t kTimeMediumMin = 10; // in ms
87 const int64_t kTimeMediumMax = 200000; // in ms
88 const uint32_t kTimeMediumBuckets = 100;
91 const int64_t kTimeLargeMin = 100; // in ms
92 const int64_t kTimeLargeMax = 2000000; // in ms
93 const uint32_t kTimeLargeBuckets = 100;
95 const int64_t kSizeKBMin = 1;
96 const int64_t kSizeKBMax = 512*1024; // very large .nexe
97 const uint32_t kSizeKBBuckets = 100;
99 const PPB_NaCl_Private* GetNaClInterface() {
100 pp::Module *module = pp::Module::Get();
102 return static_cast<const PPB_NaCl_Private*>(
103 module->GetBrowserInterface(PPB_NACL_PRIVATE_INTERFACE));
108 bool Plugin::EarlyInit(int argc, const char* argn[], const char* argv[]) {
109 PLUGIN_PRINTF(("Plugin::EarlyInit (instance=%p)\n",
110 static_cast<void*>(this)));
113 // TODO(kochi): For crbug.com/102808, this is a stopgap solution for Lion
114 // until we expose IME API to .nexe. This disables any IME interference
115 // against key inputs, so you cannot use off-the-spot IME input for NaCl apps.
116 // This makes discrepancy among platforms and therefore we should remove
117 // this hack when IME API is made available.
118 // The default for non-Mac platforms is still off-the-spot IME mode.
119 pp::TextInputController(this).SetTextInputType(PP_TEXTINPUT_TYPE_NONE);
122 // Remember the embed/object argn/argv pairs.
123 argn_ = new char*[argc];
124 argv_ = new char*[argc];
126 for (int i = 0; i < argc; ++i) {
127 if (NULL != argn_ && NULL != argv_) {
128 argn_[argc_] = strdup(argn[i]);
129 argv_[argc_] = strdup(argv[i]);
130 if (NULL == argn_[argc_] || NULL == argv_[argc_]) {
131 // Give up on passing arguments.
139 // TODO(sehr): this leaks strings if there is a subsequent failure.
141 // Set up the factory used to produce DescWrappers.
142 wrapper_factory_ = new nacl::DescWrapperFactory();
143 if (NULL == wrapper_factory_) {
146 PLUGIN_PRINTF(("Plugin::Init (wrapper_factory=%p)\n",
147 static_cast<void*>(wrapper_factory_)));
149 PLUGIN_PRINTF(("Plugin::Init (return 1)\n"));
154 void Plugin::ShutDownSubprocesses() {
155 PLUGIN_PRINTF(("Plugin::ShutDownSubprocesses (this=%p)\n",
156 static_cast<void*>(this)));
157 PLUGIN_PRINTF(("Plugin::ShutDownSubprocesses (%s)\n",
158 main_subprocess_.detailed_description().c_str()));
160 // Shut down service runtime. This must be done before all other calls so
161 // they don't block forever when waiting for the upcall thread to exit.
162 main_subprocess_.Shutdown();
164 PLUGIN_PRINTF(("Plugin::ShutDownSubprocess (this=%p, return)\n",
165 static_cast<void*>(this)));
168 void Plugin::HistogramTimeSmall(const std::string& name,
171 uma_interface_.HistogramCustomTimes(name,
173 kTimeSmallMin, kTimeSmallMax,
177 void Plugin::HistogramTimeMedium(const std::string& name,
180 uma_interface_.HistogramCustomTimes(name,
182 kTimeMediumMin, kTimeMediumMax,
186 void Plugin::HistogramTimeLarge(const std::string& name,
189 uma_interface_.HistogramCustomTimes(name,
191 kTimeLargeMin, kTimeLargeMax,
195 void Plugin::HistogramSizeKB(const std::string& name,
197 if (sample < 0) return;
198 uma_interface_.HistogramCustomCounts(name,
200 kSizeKBMin, kSizeKBMax,
204 void Plugin::HistogramEnumerate(const std::string& name,
207 int out_of_range_replacement) {
208 if (sample < 0 || sample >= maximum) {
209 if (out_of_range_replacement < 0)
210 // No replacement for bad input, abort.
213 // Use a specific value to signal a bad input.
214 sample = out_of_range_replacement;
216 uma_interface_.HistogramEnumeration(name, sample, maximum);
219 void Plugin::HistogramEnumerateOsArch(const std::string& sandbox_isa) {
233 NaClOSArch os_arch = kNaClOSArchMax;
235 os_arch = kNaClLinux32;
237 os_arch = kNaClMac32;
239 os_arch = kNaClWin32;
242 if (sandbox_isa == "x86-64")
243 os_arch = static_cast<NaClOSArch>(os_arch + 1);
244 if (sandbox_isa == "arm")
245 os_arch = static_cast<NaClOSArch>(os_arch + 2);
247 HistogramEnumerate("NaCl.Client.OSArch", os_arch, kNaClOSArchMax, -1);
250 void Plugin::HistogramEnumerateLoadStatus(PluginErrorCode error_code,
252 HistogramEnumerate("NaCl.LoadStatus.Plugin", error_code, ERROR_MAX,
255 // Gather data to see if being installed changes load outcomes.
256 const char* name = is_installed ? "NaCl.LoadStatus.Plugin.InstalledApp" :
257 "NaCl.LoadStatus.Plugin.NotInstalledApp";
258 HistogramEnumerate(name, error_code, ERROR_MAX, ERROR_UNKNOWN);
261 void Plugin::HistogramEnumerateSelLdrLoadStatus(NaClErrorCode error_code,
263 HistogramEnumerate("NaCl.LoadStatus.SelLdr", error_code,
264 NACL_ERROR_CODE_MAX, LOAD_STATUS_UNKNOWN);
266 // Gather data to see if being installed changes load outcomes.
267 const char* name = is_installed ? "NaCl.LoadStatus.SelLdr.InstalledApp" :
268 "NaCl.LoadStatus.SelLdr.NotInstalledApp";
269 HistogramEnumerate(name, error_code, NACL_ERROR_CODE_MAX,
270 LOAD_STATUS_UNKNOWN);
273 void Plugin::HistogramEnumerateManifestIsDataURI(bool is_data_uri) {
274 HistogramEnumerate("NaCl.Manifest.IsDataURI", is_data_uri, 2, -1);
277 void Plugin::HistogramHTTPStatusCode(const std::string& name,
279 // Log the status codes in rough buckets - 1XX, 2XX, etc.
280 int sample = status / 100;
281 // HTTP status codes only go up to 5XX, using "6" to indicate an internal
283 // Note: installed files may have "0" for a status code.
284 if (status < 0 || status >= 600)
286 HistogramEnumerate(name, sample, 7, 6);
289 bool Plugin::LoadNaClModuleCommon(nacl::DescWrapper* wrapper,
290 NaClSubprocess* subprocess,
291 const Manifest* manifest,
292 bool should_report_uma,
293 const SelLdrStartParams& params,
294 const pp::CompletionCallback& init_done_cb,
295 const pp::CompletionCallback& crash_cb) {
296 ServiceRuntime* new_service_runtime =
297 new ServiceRuntime(this, manifest, should_report_uma, init_done_cb,
299 subprocess->set_service_runtime(new_service_runtime);
300 PLUGIN_PRINTF(("Plugin::LoadNaClModuleCommon (service_runtime=%p)\n",
301 static_cast<void*>(new_service_runtime)));
302 if (NULL == new_service_runtime) {
303 params.error_info->SetReport(
305 "sel_ldr init failure " + subprocess->description());
309 // Now start the SelLdr instance. This must be created on the main thread.
310 pp::Core* core = pp::Module::Get()->core();
311 bool service_runtime_started;
312 if (core->IsMainThread()) {
313 StartSelLdrOnMainThread(PP_OK, new_service_runtime, params,
314 &service_runtime_started);
316 pp::CompletionCallback callback =
317 callback_factory_.NewCallback(&Plugin::StartSelLdrOnMainThread,
318 new_service_runtime, params,
319 &service_runtime_started);
320 core->CallOnMainThread(0, callback, 0);
321 new_service_runtime->WaitForSelLdrStart();
323 PLUGIN_PRINTF(("Plugin::LoadNaClModuleCommon (service_runtime_started=%d)\n",
324 service_runtime_started));
325 if (!service_runtime_started) {
329 // Now actually load the nexe, which can happen on a background thread.
330 bool nexe_loaded = new_service_runtime->LoadNexeAndStart(wrapper,
333 PLUGIN_PRINTF(("Plugin::LoadNaClModuleCommon (nexe_loaded=%d)\n",
341 void Plugin::StartSelLdrOnMainThread(int32_t pp_error,
342 ServiceRuntime* service_runtime,
343 const SelLdrStartParams& params,
345 if (pp_error != PP_OK) {
346 PLUGIN_PRINTF(("Plugin::StartSelLdrOnMainThread: non-PP_OK arg "
347 "-- SHOULD NOT HAPPEN\n"));
351 *success = service_runtime->StartSelLdr(params);
352 // Signal outside of StartSelLdr here, so that the write to *success
353 // is done before signaling.
354 service_runtime->SignalStartSelLdrDone();
357 bool Plugin::LoadNaClModule(nacl::DescWrapper* wrapper,
358 ErrorInfo* error_info,
359 bool enable_dyncode_syscalls,
360 bool enable_exception_handling,
361 bool enable_crash_throttling,
362 const pp::CompletionCallback& init_done_cb,
363 const pp::CompletionCallback& crash_cb) {
364 // Before forking a new sel_ldr process, ensure that we do not leak
365 // the ServiceRuntime object for an existing subprocess, and that any
366 // associated listener threads do not go unjoined because if they
367 // outlive the Plugin object, they will not be memory safe.
368 ShutDownSubprocesses();
369 SelLdrStartParams params(manifest_base_url(),
372 true /* uses_ppapi */,
373 enable_dev_interfaces_,
374 enable_dyncode_syscalls,
375 enable_exception_handling,
376 enable_crash_throttling);
377 if (!LoadNaClModuleCommon(wrapper, &main_subprocess_, manifest_.get(),
378 true /* should_report_uma */,
379 params, init_done_cb, crash_cb)) {
382 PLUGIN_PRINTF(("Plugin::LoadNaClModule (%s)\n",
383 main_subprocess_.detailed_description().c_str()));
387 bool Plugin::LoadNaClModuleContinuationIntern(ErrorInfo* error_info) {
388 if (!main_subprocess_.StartSrpcServices()) {
389 // The NaCl process probably crashed. On Linux, a crash causes this error,
390 // while on other platforms, the error is detected below, when we attempt to
391 // start the proxy. Report a module initialization error here, to make it
392 // less confusing for developers.
393 NaClLog(LOG_ERROR, "LoadNaClModuleContinuationIntern: "
394 "StartSrpcServices failed\n");
395 error_info->SetReport(ERROR_START_PROXY_MODULE,
396 "could not initialize module.");
399 PP_ExternalPluginResult ipc_result =
400 nacl_interface_->StartPpapiProxy(pp_instance());
401 if (ipc_result == PP_EXTERNAL_PLUGIN_OK) {
402 // Log the amound of time that has passed between the trusted plugin being
403 // initialized and the untrusted plugin being initialized. This is
404 // (roughly) the cost of using NaCl, in terms of startup time.
405 HistogramStartupTimeMedium(
406 "NaCl.Perf.StartupTime.NaClOverhead",
407 static_cast<float>(NaClGetTimeOfDayMicroseconds() - init_time_)
408 / NACL_MICROS_PER_MILLI);
409 } else if (ipc_result == PP_EXTERNAL_PLUGIN_ERROR_MODULE) {
410 NaClLog(LOG_ERROR, "LoadNaClModuleContinuationIntern: "
411 "Got PP_EXTERNAL_PLUGIN_ERROR_MODULE\n");
412 error_info->SetReport(ERROR_START_PROXY_MODULE,
413 "could not initialize module.");
415 } else if (ipc_result == PP_EXTERNAL_PLUGIN_ERROR_INSTANCE) {
416 error_info->SetReport(ERROR_START_PROXY_INSTANCE,
417 "could not create instance.");
420 PLUGIN_PRINTF(("Plugin::LoadNaClModule (%s)\n",
421 main_subprocess_.detailed_description().c_str()));
425 NaClSubprocess* Plugin::LoadHelperNaClModule(nacl::DescWrapper* wrapper,
426 const Manifest* manifest,
427 ErrorInfo* error_info) {
428 nacl::scoped_ptr<NaClSubprocess> nacl_subprocess(
429 new NaClSubprocess("helper module", NULL, NULL));
430 if (NULL == nacl_subprocess.get()) {
431 error_info->SetReport(ERROR_SEL_LDR_INIT,
432 "unable to allocate helper subprocess.");
436 // Do not report UMA stats for translator-related nexes.
437 // TODO(sehr): define new UMA stats for translator related nexe events.
438 // NOTE: The PNaCl translator nexes are not built to use the IRT. This is
439 // done to save on address space and swap space.
440 // TODO(jvoung): See if we still need the uses_ppapi variable, now that
441 // LaunchSelLdr always happens on the main thread.
442 SelLdrStartParams params(manifest_base_url(),
444 false /* uses_irt */,
445 false /* uses_ppapi */,
446 enable_dev_interfaces_,
447 false /* enable_dyncode_syscalls */,
448 false /* enable_exception_handling */,
449 true /* enable_crash_throttling */);
450 if (!LoadNaClModuleCommon(wrapper, nacl_subprocess.get(), manifest,
451 false /* should_report_uma */,
453 pp::BlockUntilComplete(),
454 pp::BlockUntilComplete())) {
457 // We need not wait for the init_done callback. We can block
458 // here in StartSrpcServices, since helper NaCl modules
459 // are spawned from a private thread.
461 // TODO(bsy): if helper module crashes, we should abort.
462 // crash_cb is not used here, so we are relying on crashes
463 // being detected in StartSrpcServices or later.
465 // NB: More refactoring might be needed, however, if helper
466 // NaCl modules have their own manifest. Currently the
467 // manifest is a per-plugin-instance object, not a per
468 // NaClSubprocess object.
469 if (!nacl_subprocess->StartSrpcServices()) {
470 error_info->SetReport(ERROR_SRPC_CONNECTION_FAIL,
471 "SRPC connection failure for " +
472 nacl_subprocess->description());
476 PLUGIN_PRINTF(("Plugin::LoadHelperNaClModule (%s)\n",
477 nacl_subprocess.get()->detailed_description().c_str()));
479 return nacl_subprocess.release();
482 char* Plugin::LookupArgument(const char* key) {
484 for (int ii = 0, len = argc_; ii < len; ++ii) {
485 if (!strcmp(keys[ii], key)) {
492 class ProgressEvent {
494 ProgressEvent(PP_NaClEventType event_type,
495 const nacl::string& url,
496 Plugin::LengthComputable length_computable,
497 uint64_t loaded_bytes,
498 uint64_t total_bytes) :
499 event_type_(event_type),
501 length_computable_(length_computable),
502 loaded_bytes_(loaded_bytes),
503 total_bytes_(total_bytes) { }
504 PP_NaClEventType event_type() const { return event_type_; }
505 const char* url() const { return url_.c_str(); }
506 Plugin::LengthComputable length_computable() const {
507 return length_computable_;
509 uint64_t loaded_bytes() const { return loaded_bytes_; }
510 uint64_t total_bytes() const { return total_bytes_; }
513 PP_NaClEventType event_type_;
515 Plugin::LengthComputable length_computable_;
516 uint64_t loaded_bytes_;
517 uint64_t total_bytes_;
520 const char* const Plugin::kNaClMIMEType = "application/x-nacl";
521 const char* const Plugin::kPnaclMIMEType = "application/x-pnacl";
523 bool Plugin::NexeIsContentHandler() const {
524 // Tests if the MIME type is not a NaCl MIME type.
525 // If the MIME type is foreign, then this NEXE is being used as a content
526 // type handler rather than directly by an HTML document.
528 !mime_type().empty() &&
529 mime_type() != kNaClMIMEType &&
530 mime_type() != kPnaclMIMEType;
534 Plugin* Plugin::New(PP_Instance pp_instance) {
535 PLUGIN_PRINTF(("Plugin::New (pp_instance=%" NACL_PRId32 ")\n", pp_instance));
536 Plugin* plugin = new Plugin(pp_instance);
537 PLUGIN_PRINTF(("Plugin::New (plugin=%p)\n", static_cast<void*>(plugin)));
542 // All failures of this function will show up as "Missing Plugin-in", so
543 // there is no need to log to JS console that there was an initialization
544 // failure. Note that module loading functions will log their own errors.
545 bool Plugin::Init(uint32_t argc, const char* argn[], const char* argv[]) {
546 PLUGIN_PRINTF(("Plugin::Init (argc=%" NACL_PRIu32 ")\n", argc));
547 HistogramEnumerateOsArch(GetSandboxISA());
548 init_time_ = NaClGetTimeOfDayMicroseconds();
550 ScriptablePlugin* scriptable_plugin = ScriptablePlugin::NewPlugin(this);
551 if (scriptable_plugin == NULL)
554 set_scriptable_plugin(scriptable_plugin);
555 PLUGIN_PRINTF(("Plugin::Init (scriptable_handle=%p)\n",
556 static_cast<void*>(scriptable_plugin_)));
557 url_util_ = pp::URLUtil_Dev::Get();
558 if (url_util_ == NULL)
561 PLUGIN_PRINTF(("Plugin::Init (url_util_=%p)\n",
562 static_cast<const void*>(url_util_)));
564 bool status = EarlyInit(static_cast<int>(argc), argn, argv);
566 // Look for the developer attribute; if it's present, enable 'dev'
568 const char* dev_settings = LookupArgument(kDevAttribute);
569 enable_dev_interfaces_ = (dev_settings != NULL);
571 const char* type_attr = LookupArgument(kTypeAttribute);
572 if (type_attr != NULL) {
573 mime_type_ = nacl::string(type_attr);
574 std::transform(mime_type_.begin(), mime_type_.end(), mime_type_.begin(),
578 const char* manifest_url = LookupArgument(kSrcManifestAttribute);
579 if (NexeIsContentHandler()) {
580 // For content handlers 'src' will be the URL for the content
581 // and 'nacl' will be the URL for the manifest.
582 manifest_url = LookupArgument(kNaClManifestAttribute);
583 // For content handlers the NEXE runs in the security context of the
584 // content it is rendering and the NEXE itself appears to be a
585 // cross-origin resource stored in a Chrome extension.
587 // Use the document URL as the base for resolving relative URLs to find the
588 // manifest. This takes into account the setting of <base> tags that
589 // precede the embed/object.
590 CHECK(url_util_ != NULL);
591 pp::Var base_var = url_util_->GetDocumentURL(this);
592 if (!base_var.is_string()) {
593 PLUGIN_PRINTF(("Plugin::Init (unable to find document url)\n"));
596 set_plugin_base_url(base_var.AsString());
597 if (manifest_url == NULL) {
598 // TODO(sehr,polina): this should be a hard error when scripting
599 // the src property is no longer allowed.
600 PLUGIN_PRINTF(("Plugin::Init:"
601 " WARNING: no 'src' property, so no manifest loaded.\n"));
602 if (NULL != LookupArgument(kNaClManifestAttribute)) {
603 PLUGIN_PRINTF(("Plugin::Init:"
604 " WARNING: 'nacl' property is incorrect. Use 'src'.\n"));
607 // Issue a GET for the manifest_url. The manifest file will be parsed to
608 // determine the nexe URL.
609 // Sets src property to full manifest URL.
610 RequestNaClManifest(manifest_url);
614 PLUGIN_PRINTF(("Plugin::Init (status=%d)\n", status));
618 Plugin::Plugin(PP_Instance pp_instance)
619 : pp::InstancePrivate(pp_instance),
620 scriptable_plugin_(NULL),
624 main_subprocess_("main subprocess", NULL, NULL),
625 nexe_error_reported_(false),
626 wrapper_factory_(NULL),
627 enable_dev_interfaces_(false),
628 is_installed_(false),
632 time_of_last_progress_event_(0),
634 nacl_interface_(NULL),
635 uma_interface_(this) {
636 PLUGIN_PRINTF(("Plugin::Plugin (this=%p, pp_instance=%"
637 NACL_PRId32 ")\n", static_cast<void*>(this), pp_instance));
638 callback_factory_.Initialize(this);
639 nexe_downloader_.Initialize(this);
640 nacl_interface_ = GetNaClInterface();
641 CHECK(nacl_interface_ != NULL);
642 set_nacl_ready_state(UNSENT);
643 set_last_error_string("");
644 // We call set_exit_status() here to ensure that the 'exitStatus' property is
645 // set. This can only be called when nacl_interface_ is not NULL.
651 int64_t shutdown_start = NaClGetTimeOfDayMicroseconds();
653 PLUGIN_PRINTF(("Plugin::~Plugin (this=%p, scriptable_plugin=%p)\n",
654 static_cast<void*>(this),
655 static_cast<void*>(scriptable_plugin())));
656 // Destroy the coordinator while the rest of the data is still there
657 pnacl_coordinator_.reset(NULL);
659 if (!nexe_error_reported()) {
661 "NaCl.ModuleUptime.Normal",
662 (shutdown_start - ready_time_) / NACL_MICROS_PER_MILLI);
665 url_downloaders_.erase(url_downloaders_.begin(), url_downloaders_.end());
667 ScriptablePlugin* scriptable_plugin_ = scriptable_plugin();
668 ScriptablePlugin::Unref(&scriptable_plugin_);
670 // ShutDownSubprocesses shuts down the main subprocess, which shuts
671 // down the main ServiceRuntime object, which kills the subprocess.
672 // As a side effect of the subprocess being killed, the reverse
673 // services thread(s) will get EOF on the reverse channel(s), and
674 // the thread(s) will exit. In ServiceRuntime::Shutdown, we invoke
675 // ReverseService::WaitForServiceThreadsToExit(), so that there will
676 // not be an extent thread(s) hanging around. This means that the
677 // ~Plugin will block until this happens. This is a requirement,
678 // since the renderer should be free to unload the plugin code, and
679 // we cannot have threads running code that gets unloaded before
682 // By waiting for the threads here, we also ensure that the Plugin
683 // object and the subprocess and ServiceRuntime objects is not
684 // (fully) destroyed while the threads are running, so resources
685 // that are destroyed after ShutDownSubprocesses (below) are
686 // guaranteed to be live and valid for access from the service
689 // The main_subprocess object, which wraps the main service_runtime
690 // object, is dtor'd implicitly after the explicit code below runs,
691 // so the main service runtime object will not have been dtor'd,
692 // though the Shutdown method may have been called, during the
693 // lifetime of the service threads.
694 ShutDownSubprocesses();
696 delete wrapper_factory_;
701 "NaCl.Perf.ShutdownTime.Total",
702 (NaClGetTimeOfDayMicroseconds() - shutdown_start)
703 / NACL_MICROS_PER_MILLI);
705 PLUGIN_PRINTF(("Plugin::~Plugin (this=%p, return)\n",
706 static_cast<void*>(this)));
709 bool Plugin::HandleDocumentLoad(const pp::URLLoader& url_loader) {
710 PLUGIN_PRINTF(("Plugin::HandleDocumentLoad (this=%p)\n",
711 static_cast<void*>(this)));
712 // We don't know if the plugin will handle the document load, but return
713 // true in order to give it a chance to respond once the proxy is started.
717 pp::Var Plugin::GetInstanceObject() {
718 PLUGIN_PRINTF(("Plugin::GetInstanceObject (this=%p)\n",
719 static_cast<void*>(this)));
720 // The browser will unref when it discards the var for this object.
721 ScriptablePlugin* handle =
722 static_cast<ScriptablePlugin*>(scriptable_plugin()->AddRef());
723 pp::Var* handle_var = handle->var();
724 PLUGIN_PRINTF(("Plugin::GetInstanceObject (handle=%p, handle_var=%p)\n",
725 static_cast<void*>(handle), static_cast<void*>(handle_var)));
726 return *handle_var; // make a copy
729 void Plugin::HistogramStartupTimeSmall(const std::string& name, float dt) {
730 if (nexe_size_ > 0) {
731 float size_in_MB = static_cast<float>(nexe_size_) / (1024.f * 1024.f);
732 HistogramTimeSmall(name, static_cast<int64_t>(dt));
733 HistogramTimeSmall(name + "PerMB", static_cast<int64_t>(dt / size_in_MB));
737 void Plugin::HistogramStartupTimeMedium(const std::string& name, float dt) {
738 if (nexe_size_ > 0) {
739 float size_in_MB = static_cast<float>(nexe_size_) / (1024.f * 1024.f);
740 HistogramTimeMedium(name, static_cast<int64_t>(dt));
741 HistogramTimeMedium(name + "PerMB", static_cast<int64_t>(dt / size_in_MB));
745 void Plugin::NexeFileDidOpen(int32_t pp_error) {
746 PLUGIN_PRINTF(("Plugin::NexeFileDidOpen (pp_error=%" NACL_PRId32 ")\n",
748 struct NaClFileInfo info = nexe_downloader_.GetFileInfo();
749 PLUGIN_PRINTF(("Plugin::NexeFileDidOpen (file_desc=%" NACL_PRId32 ")\n",
751 HistogramHTTPStatusCode(
753 "NaCl.HttpStatusCodeClass.Nexe.InstalledApp" :
754 "NaCl.HttpStatusCodeClass.Nexe.NotInstalledApp",
755 nexe_downloader_.status_code());
756 ErrorInfo error_info;
757 if (pp_error != PP_OK || info.desc == NACL_NO_FILE_DESC) {
758 if (pp_error == PP_ERROR_ABORTED) {
760 } else if (pp_error == PP_ERROR_NOACCESS) {
761 error_info.SetReport(ERROR_NEXE_NOACCESS_URL,
762 "access to nexe url was denied.");
763 ReportLoadError(error_info);
765 error_info.SetReport(ERROR_NEXE_LOAD_URL, "could not load nexe url.");
766 ReportLoadError(error_info);
770 int32_t file_desc_ok_to_close = DUP(info.desc);
771 if (file_desc_ok_to_close == NACL_NO_FILE_DESC) {
772 error_info.SetReport(ERROR_NEXE_FH_DUP,
773 "could not duplicate loaded file handle.");
774 ReportLoadError(error_info);
777 struct stat stat_buf;
778 if (0 != fstat(file_desc_ok_to_close, &stat_buf)) {
779 CLOSE(file_desc_ok_to_close);
780 error_info.SetReport(ERROR_NEXE_STAT, "could not stat nexe file.");
781 ReportLoadError(error_info);
784 size_t nexe_bytes_read = static_cast<size_t>(stat_buf.st_size);
786 nexe_size_ = nexe_bytes_read;
787 HistogramSizeKB("NaCl.Perf.Size.Nexe",
788 static_cast<int32_t>(nexe_size_ / 1024));
789 HistogramStartupTimeMedium(
790 "NaCl.Perf.StartupTime.NexeDownload",
791 static_cast<float>(nexe_downloader_.TimeSinceOpenMilliseconds()));
793 // Inform JavaScript that we successfully downloaded the nacl module.
794 EnqueueProgressEvent(PP_NACL_EVENT_PROGRESS,
795 nexe_downloader_.url_to_open(),
796 LENGTH_IS_COMPUTABLE,
800 load_start_ = NaClGetTimeOfDayMicroseconds();
801 nacl::scoped_ptr<nacl::DescWrapper>
802 wrapper(wrapper_factory()->MakeFileDesc(file_desc_ok_to_close, O_RDONLY));
803 NaClLog(4, "NexeFileDidOpen: invoking LoadNaClModule\n");
804 bool was_successful = LoadNaClModule(
805 wrapper.get(), &error_info,
806 true, /* enable_dyncode_syscalls */
807 true, /* enable_exception_handling */
808 false, /* enable_crash_throttling */
809 callback_factory_.NewCallback(&Plugin::NexeFileDidOpenContinuation),
810 callback_factory_.NewCallback(&Plugin::NexeDidCrash));
812 if (!was_successful) {
813 ReportLoadError(error_info);
817 void Plugin::NexeFileDidOpenContinuation(int32_t pp_error) {
818 ErrorInfo error_info;
821 UNREFERENCED_PARAMETER(pp_error);
822 NaClLog(4, "Entered NexeFileDidOpenContinuation\n");
823 NaClLog(4, "NexeFileDidOpenContinuation: invoking"
824 " LoadNaClModuleContinuationIntern\n");
825 was_successful = LoadNaClModuleContinuationIntern(&error_info);
826 if (was_successful) {
827 NaClLog(4, "NexeFileDidOpenContinuation: success;"
828 " setting histograms\n");
829 ready_time_ = NaClGetTimeOfDayMicroseconds();
830 HistogramStartupTimeSmall(
831 "NaCl.Perf.StartupTime.LoadModule",
832 static_cast<float>(ready_time_ - load_start_) / NACL_MICROS_PER_MILLI);
833 HistogramStartupTimeMedium(
834 "NaCl.Perf.StartupTime.Total",
835 static_cast<float>(ready_time_ - init_time_) / NACL_MICROS_PER_MILLI);
837 ReportLoadSuccess(LENGTH_IS_COMPUTABLE, nexe_size_, nexe_size_);
839 NaClLog(4, "NexeFileDidOpenContinuation: failed.");
840 ReportLoadError(error_info);
842 NaClLog(4, "Leaving NexeFileDidOpenContinuation\n");
845 static void LogLineToConsole(Plugin* plugin, const nacl::string& one_line) {
846 PLUGIN_PRINTF(("LogLineToConsole: %s\n",
848 plugin->AddToConsole(one_line);
851 void Plugin::CopyCrashLogToJsConsole() {
852 nacl::string fatal_msg(main_service_runtime()->GetCrashLogOutput());
856 PLUGIN_PRINTF(("Plugin::CopyCrashLogToJsConsole: got %" NACL_PRIuS " bytes\n",
858 while (nacl::string::npos != (ix_end = fatal_msg.find('\n', ix_start))) {
859 LogLineToConsole(this, fatal_msg.substr(ix_start, ix_end - ix_start));
860 ix_start = ix_end + 1;
862 if (ix_start != fatal_msg.size()) {
863 LogLineToConsole(this, fatal_msg.substr(ix_start));
867 void Plugin::NexeDidCrash(int32_t pp_error) {
868 PLUGIN_PRINTF(("Plugin::NexeDidCrash (pp_error=%" NACL_PRId32 ")\n",
870 if (pp_error != PP_OK) {
871 PLUGIN_PRINTF(("Plugin::NexeDidCrash: CallOnMainThread callback with"
872 " non-PP_OK arg -- SHOULD NOT HAPPEN\n"));
874 PLUGIN_PRINTF(("Plugin::NexeDidCrash: crash event!\n"));
875 if (-1 != exit_status()) {
876 // The NaCl module voluntarily exited. However, this is still a
877 // crash from the point of view of Pepper, since PPAPI plugins are
878 // event handlers and should never exit.
879 PLUGIN_PRINTF((("Plugin::NexeDidCrash: nexe exited with status %d"
880 " so this is a \"controlled crash\".\n"),
883 // If the crash occurs during load, we just want to report an error
884 // that fits into our load progress event grammar. If the crash
885 // occurs after loaded/loadend, then we use ReportDeadNexe to send a
887 if (nexe_error_reported()) {
888 PLUGIN_PRINTF(("Plugin::NexeDidCrash: error already reported;"
891 if (nacl_ready_state_ == DONE) {
894 ErrorInfo error_info;
895 // The error is not quite right. In particular, the crash
896 // reported by this path could be due to NaCl application
897 // crashes that occur after the PPAPI proxy has started.
898 error_info.SetReport(ERROR_START_PROXY_CRASH,
899 "Nexe crashed during startup");
900 ReportLoadError(error_info);
904 // In all cases, try to grab the crash log. The first error
905 // reported may have come from the start_module RPC reply indicating
906 // a validation error or something similar, which wouldn't grab the
907 // crash log. In the event that this is called twice, the second
908 // invocation will just be a no-op, since all the crash log will
909 // have been received and we'll just get an EOF indication.
910 CopyCrashLogToJsConsole();
913 void Plugin::BitcodeDidTranslate(int32_t pp_error) {
914 PLUGIN_PRINTF(("Plugin::BitcodeDidTranslate (pp_error=%" NACL_PRId32 ")\n",
916 if (pp_error != PP_OK) {
917 // Error should have been reported by pnacl. Just return.
918 PLUGIN_PRINTF(("Plugin::BitcodeDidTranslate error in Pnacl\n"));
922 // Inform JavaScript that we successfully translated the bitcode to a nexe.
923 nacl::scoped_ptr<nacl::DescWrapper>
924 wrapper(pnacl_coordinator_.get()->ReleaseTranslatedFD());
925 ErrorInfo error_info;
926 bool was_successful = LoadNaClModule(
927 wrapper.get(), &error_info,
928 false, /* enable_dyncode_syscalls */
929 false, /* enable_exception_handling */
930 true, /* enable_crash_throttling */
931 callback_factory_.NewCallback(&Plugin::BitcodeDidTranslateContinuation),
932 callback_factory_.NewCallback(&Plugin::NexeDidCrash));
934 if (!was_successful) {
935 ReportLoadError(error_info);
939 void Plugin::BitcodeDidTranslateContinuation(int32_t pp_error) {
940 ErrorInfo error_info;
941 bool was_successful = LoadNaClModuleContinuationIntern(&error_info);
943 NaClLog(4, "Entered BitcodeDidTranslateContinuation\n");
944 UNREFERENCED_PARAMETER(pp_error);
945 if (was_successful) {
948 pnacl_coordinator_->GetCurrentProgress(&loaded, &total);
949 ReportLoadSuccess(LENGTH_IS_COMPUTABLE, loaded, total);
951 ReportLoadError(error_info);
955 void Plugin::ReportDeadNexe() {
956 PLUGIN_PRINTF(("Plugin::ReportDeadNexe\n"));
958 if (nacl_ready_state_ == DONE && !nexe_error_reported()) { // After loadEnd.
959 int64_t crash_time = NaClGetTimeOfDayMicroseconds();
960 // Crashes will be more likely near startup, so use a medium histogram
961 // instead of a large one.
963 "NaCl.ModuleUptime.Crash",
964 (crash_time - ready_time_) / NACL_MICROS_PER_MILLI);
966 nacl::string message = nacl::string("NaCl module crashed");
967 set_last_error_string(message);
968 AddToConsole(message);
970 EnqueueProgressEvent(PP_NACL_EVENT_CRASH);
971 set_nexe_error_reported(true);
973 // else ReportLoadError() and ReportAbortError() will be used by loading code
974 // to provide error handling.
976 // NOTE: not all crashes during load will make it here.
977 // Those in BrowserPpp::InitializeModule and creation of PPP interfaces
978 // will just get reported back as PP_ERROR_FAILED.
981 void Plugin::NaClManifestBufferReady(int32_t pp_error) {
982 PLUGIN_PRINTF(("Plugin::NaClManifestBufferReady (pp_error=%"
983 NACL_PRId32 ")\n", pp_error));
984 ErrorInfo error_info;
985 set_manifest_url(nexe_downloader_.url());
986 if (pp_error != PP_OK) {
987 if (pp_error == PP_ERROR_ABORTED) {
990 error_info.SetReport(ERROR_MANIFEST_LOAD_URL,
991 "could not load manifest url.");
992 ReportLoadError(error_info);
997 const std::deque<char>& buffer = nexe_downloader_.buffer();
998 size_t buffer_size = buffer.size();
999 if (buffer_size > kNaClManifestMaxFileBytes) {
1000 error_info.SetReport(ERROR_MANIFEST_TOO_LARGE,
1001 "manifest file too large.");
1002 ReportLoadError(error_info);
1005 nacl::scoped_array<char> json_buffer(new char[buffer_size + 1]);
1006 if (json_buffer == NULL) {
1007 error_info.SetReport(ERROR_MANIFEST_MEMORY_ALLOC,
1008 "could not allocate manifest memory.");
1009 ReportLoadError(error_info);
1012 std::copy(buffer.begin(), buffer.begin() + buffer_size, &json_buffer[0]);
1013 json_buffer[buffer_size] = '\0';
1015 ProcessNaClManifest(json_buffer.get());
1018 void Plugin::NaClManifestFileDidOpen(int32_t pp_error) {
1019 PLUGIN_PRINTF(("Plugin::NaClManifestFileDidOpen (pp_error=%"
1020 NACL_PRId32 ")\n", pp_error));
1021 HistogramTimeSmall("NaCl.Perf.StartupTime.ManifestDownload",
1022 nexe_downloader_.TimeSinceOpenMilliseconds());
1023 HistogramHTTPStatusCode(
1025 "NaCl.HttpStatusCodeClass.Manifest.InstalledApp" :
1026 "NaCl.HttpStatusCodeClass.Manifest.NotInstalledApp",
1027 nexe_downloader_.status_code());
1028 ErrorInfo error_info;
1029 // The manifest file was successfully opened. Set the src property on the
1030 // plugin now, so that the full url is available to error handlers.
1031 set_manifest_url(nexe_downloader_.url());
1032 struct NaClFileInfo info = nexe_downloader_.GetFileInfo();
1033 PLUGIN_PRINTF(("Plugin::NaClManifestFileDidOpen (file_desc=%"
1034 NACL_PRId32 ")\n", info.desc));
1035 if (pp_error != PP_OK || info.desc == NACL_NO_FILE_DESC) {
1036 if (pp_error == PP_ERROR_ABORTED) {
1038 } else if (pp_error == PP_ERROR_NOACCESS) {
1039 error_info.SetReport(ERROR_MANIFEST_NOACCESS_URL,
1040 "access to manifest url was denied.");
1041 ReportLoadError(error_info);
1043 error_info.SetReport(ERROR_MANIFEST_LOAD_URL,
1044 "could not load manifest url.");
1045 ReportLoadError(error_info);
1049 // SlurpFile closes the file descriptor after reading (or on error).
1050 // Duplicate our file descriptor since it will be handled by the browser.
1051 int dup_file_desc = DUP(info.desc);
1052 nacl::string json_buffer;
1053 file_utils::StatusCode status = file_utils::SlurpFile(
1054 dup_file_desc, json_buffer, kNaClManifestMaxFileBytes);
1056 if (status != file_utils::PLUGIN_FILE_SUCCESS) {
1058 case file_utils::PLUGIN_FILE_SUCCESS:
1061 case file_utils::PLUGIN_FILE_ERROR_MEM_ALLOC:
1062 error_info.SetReport(ERROR_MANIFEST_MEMORY_ALLOC,
1063 "could not allocate manifest memory.");
1065 case file_utils::PLUGIN_FILE_ERROR_OPEN:
1066 error_info.SetReport(ERROR_MANIFEST_OPEN,
1067 "could not open manifest file.");
1069 case file_utils::PLUGIN_FILE_ERROR_FILE_TOO_LARGE:
1070 error_info.SetReport(ERROR_MANIFEST_TOO_LARGE,
1071 "manifest file too large.");
1073 case file_utils::PLUGIN_FILE_ERROR_STAT:
1074 error_info.SetReport(ERROR_MANIFEST_STAT,
1075 "could not stat manifest file.");
1077 case file_utils::PLUGIN_FILE_ERROR_READ:
1078 error_info.SetReport(ERROR_MANIFEST_READ,
1079 "could not read manifest file.");
1082 ReportLoadError(error_info);
1086 ProcessNaClManifest(json_buffer);
1089 void Plugin::ProcessNaClManifest(const nacl::string& manifest_json) {
1090 HistogramSizeKB("NaCl.Perf.Size.Manifest",
1091 static_cast<int32_t>(manifest_json.length() / 1024));
1092 nacl::string program_url;
1093 PnaclOptions pnacl_options;
1094 ErrorInfo error_info;
1095 if (!SetManifestObject(manifest_json, &error_info)) {
1096 ReportLoadError(error_info);
1100 if (manifest_->GetProgramURL(&program_url, &pnacl_options, &error_info)) {
1101 is_installed_ = GetUrlScheme(program_url) == SCHEME_CHROME_EXTENSION;
1102 set_nacl_ready_state(LOADING);
1103 // Inform JavaScript that we found a nexe URL to load.
1104 EnqueueProgressEvent(PP_NACL_EVENT_PROGRESS);
1105 if (pnacl_options.translate()) {
1106 pp::CompletionCallback translate_callback =
1107 callback_factory_.NewCallback(&Plugin::BitcodeDidTranslate);
1108 // Will always call the callback on success or failure.
1109 pnacl_coordinator_.reset(
1110 PnaclCoordinator::BitcodeToNative(this,
1113 translate_callback));
1116 // Try the fast path first. This will only block if the file is installed.
1117 if (OpenURLFast(program_url, &nexe_downloader_)) {
1118 NexeFileDidOpen(PP_OK);
1120 pp::CompletionCallback open_callback =
1121 callback_factory_.NewCallback(&Plugin::NexeFileDidOpen);
1122 // Will always call the callback on success or failure.
1124 nexe_downloader_.Open(program_url,
1128 &UpdateDownloadProgress));
1133 // Failed to select the program and/or the translator.
1134 ReportLoadError(error_info);
1137 void Plugin::RequestNaClManifest(const nacl::string& url) {
1138 PLUGIN_PRINTF(("Plugin::RequestNaClManifest (url='%s')\n", url.c_str()));
1139 PLUGIN_PRINTF(("Plugin::RequestNaClManifest (plugin base url='%s')\n",
1140 plugin_base_url().c_str()));
1141 // The full URL of the manifest file is relative to the base url.
1142 CHECK(url_util_ != NULL);
1143 pp::Var nmf_resolved_url =
1144 url_util_->ResolveRelativeToURL(plugin_base_url(), pp::Var(url));
1145 if (!nmf_resolved_url.is_string()) {
1146 ErrorInfo error_info;
1147 error_info.SetReport(
1148 ERROR_MANIFEST_RESOLVE_URL,
1149 nacl::string("could not resolve URL \"") + url.c_str() +
1150 "\" relative to \"" + plugin_base_url().c_str() + "\".");
1151 ReportLoadError(error_info);
1154 PLUGIN_PRINTF(("Plugin::RequestNaClManifest (resolved url='%s')\n",
1155 nmf_resolved_url.AsString().c_str()));
1156 is_installed_ = GetUrlScheme(nmf_resolved_url.AsString()) ==
1157 SCHEME_CHROME_EXTENSION;
1158 set_manifest_base_url(nmf_resolved_url.AsString());
1159 set_manifest_url(url);
1160 // Inform JavaScript that a load is starting.
1161 set_nacl_ready_state(OPENED);
1162 EnqueueProgressEvent(PP_NACL_EVENT_LOADSTART);
1163 bool is_data_uri = GetUrlScheme(nmf_resolved_url.AsString()) == SCHEME_DATA;
1164 HistogramEnumerateManifestIsDataURI(static_cast<int>(is_data_uri));
1166 pp::CompletionCallback open_callback =
1167 callback_factory_.NewCallback(&Plugin::NaClManifestBufferReady);
1168 // Will always call the callback on success or failure.
1169 CHECK(nexe_downloader_.Open(nmf_resolved_url.AsString(),
1175 pp::CompletionCallback open_callback =
1176 callback_factory_.NewCallback(&Plugin::NaClManifestFileDidOpen);
1177 // Will always call the callback on success or failure.
1178 CHECK(nexe_downloader_.Open(nmf_resolved_url.AsString(),
1187 bool Plugin::SetManifestObject(const nacl::string& manifest_json,
1188 ErrorInfo* error_info) {
1189 PLUGIN_PRINTF(("Plugin::SetManifestObject(): manifest_json='%s'.\n",
1190 manifest_json.c_str()));
1191 if (error_info == NULL)
1193 // Determine whether lookups should use portable (i.e., pnacl versions)
1194 // rather than platform-specific files.
1195 bool is_pnacl = (mime_type() == kPnaclMIMEType);
1196 nacl::scoped_ptr<JsonManifest> json_manifest(
1197 new JsonManifest(url_util_,
1198 manifest_base_url(),
1199 (is_pnacl ? kPortableISA : GetSandboxISA())));
1200 if (!json_manifest->Init(manifest_json, error_info)) {
1203 manifest_.reset(json_manifest.release());
1207 void Plugin::UrlDidOpenForStreamAsFile(int32_t pp_error,
1208 FileDownloader*& url_downloader,
1209 PP_CompletionCallback callback) {
1210 PLUGIN_PRINTF(("Plugin::UrlDidOpen (pp_error=%" NACL_PRId32
1211 ", url_downloader=%p)\n", pp_error,
1212 static_cast<void*>(url_downloader)));
1213 url_downloaders_.erase(url_downloader);
1214 nacl::scoped_ptr<FileDownloader> scoped_url_downloader(url_downloader);
1215 struct NaClFileInfo info = scoped_url_downloader->GetFileInfo();
1217 if (pp_error != PP_OK) {
1218 PP_RunCompletionCallback(&callback, pp_error);
1219 } else if (info.desc > NACL_NO_FILE_DESC) {
1220 url_file_info_map_[url_downloader->url_to_open()] = info;
1221 PP_RunCompletionCallback(&callback, PP_OK);
1223 PP_RunCompletionCallback(&callback, PP_ERROR_FAILED);
1227 struct NaClFileInfo Plugin::GetFileInfo(const nacl::string& url) {
1228 struct NaClFileInfo info;
1229 memset(&info, 0, sizeof(info));
1230 std::map<nacl::string, struct NaClFileInfo>::iterator it =
1231 url_file_info_map_.find(url);
1232 if (it != url_file_info_map_.end()) {
1234 info.desc = DUP(info.desc);
1241 bool Plugin::StreamAsFile(const nacl::string& url,
1242 PP_CompletionCallback callback) {
1243 PLUGIN_PRINTF(("Plugin::StreamAsFile (url='%s')\n", url.c_str()));
1244 FileDownloader* downloader = new FileDownloader();
1245 downloader->Initialize(this);
1246 url_downloaders_.insert(downloader);
1247 // Untrusted loads are always relative to the page's origin.
1248 CHECK(url_util_ != NULL);
1249 pp::Var resolved_url =
1250 url_util_->ResolveRelativeToURL(pp::Var(plugin_base_url()), url);
1251 if (!resolved_url.is_string()) {
1252 PLUGIN_PRINTF(("Plugin::StreamAsFile: "
1253 "could not resolve url \"%s\" relative to plugin \"%s\".",
1255 plugin_base_url().c_str()));
1259 // Try the fast path first. This will only block if the file is installed.
1260 if (OpenURLFast(url, downloader)) {
1261 UrlDidOpenForStreamAsFile(PP_OK, downloader, callback);
1265 pp::CompletionCallback open_callback = callback_factory_.NewCallback(
1266 &Plugin::UrlDidOpenForStreamAsFile, downloader, callback);
1267 // If true, will always call the callback on success or failure.
1268 return downloader->Open(url,
1272 &UpdateDownloadProgress);
1276 void Plugin::ReportLoadSuccess(LengthComputable length_computable,
1277 uint64_t loaded_bytes,
1278 uint64_t total_bytes) {
1279 // Set the readyState attribute to indicate loaded.
1280 set_nacl_ready_state(DONE);
1281 // Inform JavaScript that loading was successful and is complete.
1282 const nacl::string& url = nexe_downloader_.url_to_open();
1283 EnqueueProgressEvent(
1284 PP_NACL_EVENT_LOAD, url, length_computable, loaded_bytes, total_bytes);
1285 EnqueueProgressEvent(
1286 PP_NACL_EVENT_LOADEND, url, length_computable, loaded_bytes, total_bytes);
1289 HistogramEnumerateLoadStatus(ERROR_LOAD_SUCCESS, is_installed_);
1293 void Plugin::ReportLoadError(const ErrorInfo& error_info) {
1294 PLUGIN_PRINTF(("Plugin::ReportLoadError (error='%s')\n",
1295 error_info.message().c_str()));
1296 // For errors the user (and not just the developer) should know about,
1297 // report them to the renderer so the browser can display a message.
1298 if (error_info.error_code() == ERROR_MANIFEST_PROGRAM_MISSING_ARCH) {
1299 // A special case: the manifest may otherwise be valid but is missing
1300 // a program/file compatible with the user's sandbox.
1301 nacl_interface()->ReportNaClError(pp_instance(),
1302 PP_NACL_MANIFEST_MISSING_ARCH);
1305 // Set the readyState attribute to indicate we need to start over.
1306 set_nacl_ready_state(DONE);
1307 set_nexe_error_reported(true);
1308 // Report an error in lastError and on the JavaScript console.
1309 nacl::string message = nacl::string("NaCl module load failed: ") +
1310 error_info.message();
1311 set_last_error_string(message);
1312 AddToConsole(nacl::string("NaCl module load failed: ") +
1313 error_info.console_message());
1314 // Inform JavaScript that loading encountered an error and is complete.
1315 EnqueueProgressEvent(PP_NACL_EVENT_ERROR);
1316 EnqueueProgressEvent(PP_NACL_EVENT_LOADEND);
1319 HistogramEnumerateLoadStatus(error_info.error_code(), is_installed_);
1323 void Plugin::ReportLoadAbort() {
1324 PLUGIN_PRINTF(("Plugin::ReportLoadAbort\n"));
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 error_string("NaCl module load failed: user aborted");
1330 set_last_error_string(error_string);
1331 AddToConsole(error_string);
1332 // Inform JavaScript that loading was aborted and is complete.
1333 EnqueueProgressEvent(PP_NACL_EVENT_ABORT);
1334 EnqueueProgressEvent(PP_NACL_EVENT_LOADEND);
1337 HistogramEnumerateLoadStatus(ERROR_LOAD_ABORTED, is_installed_);
1340 void Plugin::UpdateDownloadProgress(
1341 PP_Instance pp_instance,
1342 PP_Resource pp_resource,
1343 int64_t /*bytes_sent*/,
1344 int64_t /*total_bytes_to_be_sent*/,
1345 int64_t bytes_received,
1346 int64_t total_bytes_to_be_received) {
1347 Instance* instance = pp::Module::Get()->InstanceForPPInstance(pp_instance);
1348 if (instance != NULL) {
1349 Plugin* plugin = static_cast<Plugin*>(instance);
1350 // Rate limit progress events to a maximum of 100 per second.
1351 int64_t time = NaClGetTimeOfDayMicroseconds();
1352 int64_t elapsed = time - plugin->time_of_last_progress_event_;
1353 const int64_t kTenMilliseconds = 10000;
1354 if (elapsed > kTenMilliseconds) {
1355 plugin->time_of_last_progress_event_ = time;
1357 // Find the URL loader that sent this notification.
1358 const FileDownloader* file_downloader =
1359 plugin->FindFileDownloader(pp_resource);
1360 // If not a streamed file, it must be the .nexe loader.
1361 if (file_downloader == NULL)
1362 file_downloader = &plugin->nexe_downloader_;
1363 nacl::string url = file_downloader->url_to_open();
1364 LengthComputable length_computable = (total_bytes_to_be_received >= 0) ?
1365 LENGTH_IS_COMPUTABLE : LENGTH_IS_NOT_COMPUTABLE;
1367 plugin->EnqueueProgressEvent(PP_NACL_EVENT_PROGRESS,
1371 total_bytes_to_be_received);
1376 const FileDownloader* Plugin::FindFileDownloader(
1377 PP_Resource url_loader) const {
1378 const FileDownloader* file_downloader = NULL;
1379 if (url_loader == nexe_downloader_.url_loader()) {
1380 file_downloader = &nexe_downloader_;
1382 std::set<FileDownloader*>::const_iterator it = url_downloaders_.begin();
1383 while (it != url_downloaders_.end()) {
1384 if (url_loader == (*it)->url_loader()) {
1385 file_downloader = (*it);
1391 return file_downloader;
1394 void Plugin::EnqueueProgressEvent(PP_NaClEventType event_type) {
1395 EnqueueProgressEvent(event_type,
1397 Plugin::LENGTH_IS_NOT_COMPUTABLE,
1398 Plugin::kUnknownBytes,
1399 Plugin::kUnknownBytes);
1402 void Plugin::EnqueueProgressEvent(PP_NaClEventType event_type,
1403 const nacl::string& url,
1404 LengthComputable length_computable,
1405 uint64_t loaded_bytes,
1406 uint64_t total_bytes) {
1407 PLUGIN_PRINTF(("Plugin::EnqueueProgressEvent ("
1408 "event_type='%d', url='%s', length_computable=%d, "
1409 "loaded=%" NACL_PRIu64 ", total=%" NACL_PRIu64 ")\n",
1410 static_cast<int>(event_type),
1412 static_cast<int>(length_computable),
1416 progress_events_.push(new ProgressEvent(event_type,
1421 // Note that using callback_factory_ in this way is not thread safe.
1422 // If/when EnqueueProgressEvent is callable from another thread, this
1423 // will need to change.
1424 pp::CompletionCallback callback =
1425 callback_factory_.NewCallback(&Plugin::DispatchProgressEvent);
1426 pp::Core* core = pp::Module::Get()->core();
1427 core->CallOnMainThread(0, callback, 0);
1430 void Plugin::ReportSelLdrLoadStatus(int status) {
1431 HistogramEnumerateSelLdrLoadStatus(static_cast<NaClErrorCode>(status),
1435 void Plugin::DispatchProgressEvent(int32_t result) {
1436 PLUGIN_PRINTF(("Plugin::DispatchProgressEvent (result=%"
1437 NACL_PRId32 ")\n", result));
1441 if (progress_events_.empty()) {
1442 PLUGIN_PRINTF(("Plugin::DispatchProgressEvent: no pending events\n"));
1445 nacl::scoped_ptr<ProgressEvent> event(progress_events_.front());
1446 progress_events_.pop();
1447 PLUGIN_PRINTF(("Plugin::DispatchProgressEvent ("
1448 "event_type='%d', url='%s', length_computable=%d, "
1449 "loaded=%" NACL_PRIu64 ", total=%" NACL_PRIu64 ")\n",
1450 static_cast<int>(event->event_type()),
1452 static_cast<int>(event->length_computable()),
1453 event->loaded_bytes(),
1454 event->total_bytes()));
1456 nacl_interface_->DispatchEvent(
1458 event->event_type(),
1459 pp::Var(event->url()).pp_var(),
1460 event->length_computable() == LENGTH_IS_COMPUTABLE ? PP_TRUE : PP_FALSE,
1461 event->loaded_bytes(),
1462 event->total_bytes());
1465 bool Plugin::OpenURLFast(const nacl::string& url,
1466 FileDownloader* downloader) {
1467 // Fast path only works for installed file URLs.
1468 if (GetUrlScheme(url) != SCHEME_CHROME_EXTENSION)
1470 // IMPORTANT: Make sure the document can request the given URL. If we don't
1471 // check, a malicious app could probe the extension system. This enforces a
1472 // same-origin policy which prevents the app from requesting resources from
1474 if (!DocumentCanRequest(url))
1477 uint64_t file_token_lo = 0;
1478 uint64_t file_token_hi = 0;
1479 PP_FileHandle file_handle =
1480 nacl_interface()->OpenNaClExecutable(pp_instance(),
1482 &file_token_lo, &file_token_hi);
1483 // We shouldn't hit this if the file URL is in an installed app.
1484 if (file_handle == PP_kInvalidFileHandle)
1487 // FileDownloader takes ownership of the file handle.
1488 downloader->OpenFast(url, file_handle, file_token_lo, file_token_hi);
1492 UrlSchemeType Plugin::GetUrlScheme(const std::string& url) {
1493 CHECK(url_util_ != NULL);
1494 PP_URLComponents_Dev comps;
1495 pp::Var canonicalized =
1496 url_util_->Canonicalize(pp::Var(url), &comps);
1498 if (canonicalized.is_null() ||
1499 (comps.scheme.begin == 0 && comps.scheme.len == -1)) {
1500 // |url| was an invalid URL or has no scheme.
1501 return SCHEME_OTHER;
1504 CHECK(comps.scheme.begin <
1505 static_cast<int>(canonicalized.AsString().size()));
1506 CHECK(comps.scheme.begin + comps.scheme.len <
1507 static_cast<int>(canonicalized.AsString().size()));
1509 std::string scheme = canonicalized.AsString().substr(comps.scheme.begin,
1511 if (scheme == kChromeExtensionUriScheme)
1512 return SCHEME_CHROME_EXTENSION;
1513 if (scheme == kDataUriScheme)
1515 return SCHEME_OTHER;
1518 bool Plugin::DocumentCanRequest(const std::string& url) {
1519 CHECK(url_util_ != NULL);
1520 return url_util_->DocumentCanRequest(this, pp::Var(url));
1523 void Plugin::AddToConsole(const nacl::string& text) {
1524 pp::Module* module = pp::Module::Get();
1525 const PPB_Var* var_interface =
1526 static_cast<const PPB_Var*>(
1527 module->GetBrowserInterface(PPB_VAR_INTERFACE));
1528 nacl::string prefix_string("NativeClient");
1530 var_interface->VarFromUtf8(prefix_string.c_str(),
1531 static_cast<uint32_t>(prefix_string.size()));
1532 PP_Var str = var_interface->VarFromUtf8(text.c_str(),
1533 static_cast<uint32_t>(text.size()));
1534 const PPB_Console* console_interface =
1535 static_cast<const PPB_Console*>(
1536 module->GetBrowserInterface(PPB_CONSOLE_INTERFACE));
1537 console_interface->LogWithSource(pp_instance(), PP_LOGLEVEL_LOG, prefix, str);
1538 var_interface->Release(prefix);
1539 var_interface->Release(str);
1542 void Plugin::set_last_error_string(const nacl::string& error) {
1543 DCHECK(nacl_interface_);
1544 nacl_interface_->SetReadOnlyProperty(pp_instance(),
1545 pp::Var("lastError").pp_var(),
1546 pp::Var(error).pp_var());
1549 void Plugin::set_nacl_ready_state(ReadyState state) {
1550 nacl_ready_state_ = state;
1551 DCHECK(nacl_interface_);
1552 nacl_interface_->SetReadOnlyProperty(pp_instance(),
1553 pp::Var("readyState").pp_var(),
1554 pp::Var(state).pp_var());
1557 void Plugin::set_exit_status(int exit_status) {
1558 pp::Core* core = pp::Module::Get()->core();
1559 if (core->IsMainThread()) {
1560 SetExitStatusOnMainThread(PP_OK, exit_status);
1562 pp::CompletionCallback callback =
1563 callback_factory_.NewCallback(&Plugin::SetExitStatusOnMainThread,
1565 core->CallOnMainThread(0, callback, 0);
1569 void Plugin::SetExitStatusOnMainThread(int32_t pp_error,
1571 DCHECK(pp::Module::Get()->core()->IsMainThread());
1572 DCHECK(nacl_interface_);
1573 exit_status_ = exit_status;
1574 nacl_interface_->SetReadOnlyProperty(pp_instance(),
1575 pp::Var("exitStatus").pp_var(),
1576 pp::Var(exit_status_).pp_var());
1580 } // namespace plugin