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/service_runtime.h"
48 #include "ppapi/native_client/src/trusted/plugin/utility.h"
54 const char* const kTypeAttribute = "type";
55 // The "src" attribute of the <embed> tag. The value is expected to be either
56 // a URL or URI pointing to the manifest file (which is expected to contain
57 // JSON matching ISAs with .nexe URLs).
58 const char* const kSrcManifestAttribute = "src";
59 // The "nacl" attribute of the <embed> tag. We use the value of this attribute
60 // to find the manifest file when NaCl is registered as a plug-in for another
61 // MIME type because the "src" attribute is used to supply us with the resource
62 // of that MIME type that we're supposed to display.
63 const char* const kNaClManifestAttribute = "nacl";
64 // The pseudo-ISA used to indicate portable native client.
65 const char* const kPortableISA = "portable";
66 // This is a pretty arbitrary limit on the byte size of the NaCl manfest file.
67 // Note that the resulting string object has to have at least one byte extra
68 // for the null termination character.
69 const size_t kNaClManifestMaxFileBytes = 1024 * 1024;
71 // Define an argument name to enable 'dev' interfaces. To make sure it doesn't
72 // collide with any user-defined HTML attribute, make the first character '@'.
73 const char* const kDevAttribute = "@dev";
75 // URL schemes that we treat in special ways.
76 const char* const kChromeExtensionUriScheme = "chrome-extension";
77 const char* const kDataUriScheme = "data";
80 const int64_t kTimeSmallMin = 1; // in ms
81 const int64_t kTimeSmallMax = 20000; // in ms
82 const uint32_t kTimeSmallBuckets = 100;
84 // Up to 3 minutes, 20 seconds
85 const int64_t kTimeMediumMin = 10; // in ms
86 const int64_t kTimeMediumMax = 200000; // in ms
87 const uint32_t kTimeMediumBuckets = 100;
90 const int64_t kTimeLargeMin = 100; // in ms
91 const int64_t kTimeLargeMax = 2000000; // in ms
92 const uint32_t kTimeLargeBuckets = 100;
94 const int64_t kSizeKBMin = 1;
95 const int64_t kSizeKBMax = 512*1024; // very large .nexe
96 const uint32_t kSizeKBBuckets = 100;
98 const PPB_NaCl_Private* GetNaClInterface() {
99 pp::Module *module = pp::Module::Get();
101 return static_cast<const PPB_NaCl_Private*>(
102 module->GetBrowserInterface(PPB_NACL_PRIVATE_INTERFACE));
107 bool Plugin::EarlyInit(int argc, const char* argn[], const char* argv[]) {
108 PLUGIN_PRINTF(("Plugin::EarlyInit (instance=%p)\n",
109 static_cast<void*>(this)));
112 // TODO(kochi): For crbug.com/102808, this is a stopgap solution for Lion
113 // until we expose IME API to .nexe. This disables any IME interference
114 // against key inputs, so you cannot use off-the-spot IME input for NaCl apps.
115 // This makes discrepancy among platforms and therefore we should remove
116 // this hack when IME API is made available.
117 // The default for non-Mac platforms is still off-the-spot IME mode.
118 pp::TextInputController(this).SetTextInputType(PP_TEXTINPUT_TYPE_NONE);
121 for (int i = 0; i < argc; ++i) {
122 std::string name(argn[i]);
123 std::string value(argv[i]);
127 // Set up the factory used to produce DescWrappers.
128 wrapper_factory_ = new nacl::DescWrapperFactory();
129 if (NULL == wrapper_factory_) {
132 PLUGIN_PRINTF(("Plugin::Init (wrapper_factory=%p)\n",
133 static_cast<void*>(wrapper_factory_)));
135 PLUGIN_PRINTF(("Plugin::Init (return 1)\n"));
140 void Plugin::ShutDownSubprocesses() {
141 PLUGIN_PRINTF(("Plugin::ShutDownSubprocesses (this=%p)\n",
142 static_cast<void*>(this)));
143 PLUGIN_PRINTF(("Plugin::ShutDownSubprocesses (%s)\n",
144 main_subprocess_.detailed_description().c_str()));
146 // Shut down service runtime. This must be done before all other calls so
147 // they don't block forever when waiting for the upcall thread to exit.
148 main_subprocess_.Shutdown();
150 PLUGIN_PRINTF(("Plugin::ShutDownSubprocess (this=%p, return)\n",
151 static_cast<void*>(this)));
154 void Plugin::HistogramTimeSmall(const std::string& name,
157 uma_interface_.HistogramCustomTimes(name,
159 kTimeSmallMin, kTimeSmallMax,
163 void Plugin::HistogramTimeMedium(const std::string& name,
166 uma_interface_.HistogramCustomTimes(name,
168 kTimeMediumMin, kTimeMediumMax,
172 void Plugin::HistogramTimeLarge(const std::string& name,
175 uma_interface_.HistogramCustomTimes(name,
177 kTimeLargeMin, kTimeLargeMax,
181 void Plugin::HistogramSizeKB(const std::string& name,
183 if (sample < 0) return;
184 uma_interface_.HistogramCustomCounts(name,
186 kSizeKBMin, kSizeKBMax,
190 void Plugin::HistogramEnumerate(const std::string& name,
193 int out_of_range_replacement) {
194 if (sample < 0 || sample >= maximum) {
195 if (out_of_range_replacement < 0)
196 // No replacement for bad input, abort.
199 // Use a specific value to signal a bad input.
200 sample = out_of_range_replacement;
202 uma_interface_.HistogramEnumeration(name, sample, maximum);
205 void Plugin::HistogramEnumerateOsArch(const std::string& sandbox_isa) {
219 NaClOSArch os_arch = kNaClOSArchMax;
221 os_arch = kNaClLinux32;
223 os_arch = kNaClMac32;
225 os_arch = kNaClWin32;
228 if (sandbox_isa == "x86-64")
229 os_arch = static_cast<NaClOSArch>(os_arch + 1);
230 if (sandbox_isa == "arm")
231 os_arch = static_cast<NaClOSArch>(os_arch + 2);
233 HistogramEnumerate("NaCl.Client.OSArch", os_arch, kNaClOSArchMax, -1);
236 void Plugin::HistogramEnumerateLoadStatus(PluginErrorCode error_code,
238 HistogramEnumerate("NaCl.LoadStatus.Plugin", error_code, ERROR_MAX,
241 // Gather data to see if being installed changes load outcomes.
242 const char* name = is_installed ? "NaCl.LoadStatus.Plugin.InstalledApp" :
243 "NaCl.LoadStatus.Plugin.NotInstalledApp";
244 HistogramEnumerate(name, error_code, ERROR_MAX, ERROR_UNKNOWN);
247 void Plugin::HistogramEnumerateSelLdrLoadStatus(NaClErrorCode error_code,
249 HistogramEnumerate("NaCl.LoadStatus.SelLdr", error_code,
250 NACL_ERROR_CODE_MAX, LOAD_STATUS_UNKNOWN);
252 // Gather data to see if being installed changes load outcomes.
253 const char* name = is_installed ? "NaCl.LoadStatus.SelLdr.InstalledApp" :
254 "NaCl.LoadStatus.SelLdr.NotInstalledApp";
255 HistogramEnumerate(name, error_code, NACL_ERROR_CODE_MAX,
256 LOAD_STATUS_UNKNOWN);
259 void Plugin::HistogramEnumerateManifestIsDataURI(bool is_data_uri) {
260 HistogramEnumerate("NaCl.Manifest.IsDataURI", is_data_uri, 2, -1);
263 void Plugin::HistogramHTTPStatusCode(const std::string& name,
265 // Log the status codes in rough buckets - 1XX, 2XX, etc.
266 int sample = status / 100;
267 // HTTP status codes only go up to 5XX, using "6" to indicate an internal
269 // Note: installed files may have "0" for a status code.
270 if (status < 0 || status >= 600)
272 HistogramEnumerate(name, sample, 7, 6);
275 bool Plugin::LoadNaClModuleFromBackgroundThread(
276 nacl::DescWrapper* wrapper,
277 NaClSubprocess* subprocess,
278 const Manifest* manifest,
279 const SelLdrStartParams& params) {
280 CHECK(!pp::Module::Get()->core()->IsMainThread());
281 ServiceRuntime* service_runtime =
282 new ServiceRuntime(this, manifest, false,
283 pp::BlockUntilComplete(), pp::BlockUntilComplete());
284 subprocess->set_service_runtime(service_runtime);
285 PLUGIN_PRINTF(("Plugin::LoadNaClModuleFromBackgroundThread "
286 "(service_runtime=%p)\n",
287 static_cast<void*>(service_runtime)));
289 // Now start the SelLdr instance. This must be created on the main thread.
290 bool service_runtime_started;
291 pp::CompletionCallback sel_ldr_callback =
292 callback_factory_.NewCallback(&Plugin::SignalStartSelLdrDone,
293 &service_runtime_started,
295 pp::CompletionCallback callback =
296 callback_factory_.NewCallback(&Plugin::StartSelLdrOnMainThread,
297 service_runtime, params,
299 pp::Module::Get()->core()->CallOnMainThread(0, callback, 0);
300 service_runtime->WaitForSelLdrStart();
301 PLUGIN_PRINTF(("Plugin::LoadNaClModuleFromBackgroundThread "
302 "(service_runtime_started=%d)\n",
303 service_runtime_started));
304 if (!service_runtime_started) {
308 // Now actually load the nexe, which can happen on a background thread.
309 bool nexe_loaded = service_runtime->LoadNexeAndStart(
310 wrapper, pp::BlockUntilComplete());
311 PLUGIN_PRINTF(("Plugin::LoadNaClModuleFromBackgroundThread "
312 "(nexe_loaded=%d)\n",
317 void Plugin::StartSelLdrOnMainThread(int32_t pp_error,
318 ServiceRuntime* service_runtime,
319 const SelLdrStartParams& params,
320 pp::CompletionCallback callback) {
321 if (pp_error != PP_OK) {
322 PLUGIN_PRINTF(("Plugin::StartSelLdrOnMainThread: non-PP_OK arg "
323 "-- SHOULD NOT HAPPEN\n"));
324 pp::Module::Get()->core()->CallOnMainThread(0, callback, pp_error);
327 service_runtime->StartSelLdr(params, callback);
330 void Plugin::SignalStartSelLdrDone(int32_t pp_error,
332 ServiceRuntime* service_runtime) {
333 *started = (pp_error == PP_OK);
334 service_runtime->SignalStartSelLdrDone();
337 void Plugin::LoadNaClModule(nacl::DescWrapper* wrapper,
338 bool enable_dyncode_syscalls,
339 bool enable_exception_handling,
340 bool enable_crash_throttling,
341 const pp::CompletionCallback& init_done_cb,
342 const pp::CompletionCallback& crash_cb) {
343 nacl::scoped_ptr<nacl::DescWrapper> scoped_wrapper(wrapper);
344 CHECK(pp::Module::Get()->core()->IsMainThread());
345 // Before forking a new sel_ldr process, ensure that we do not leak
346 // the ServiceRuntime object for an existing subprocess, and that any
347 // associated listener threads do not go unjoined because if they
348 // outlive the Plugin object, they will not be memory safe.
349 ShutDownSubprocesses();
350 SelLdrStartParams params(manifest_base_url(),
352 true /* uses_ppapi */,
353 enable_dev_interfaces_,
354 enable_dyncode_syscalls,
355 enable_exception_handling,
356 enable_crash_throttling);
357 ErrorInfo error_info;
358 ServiceRuntime* service_runtime =
359 new ServiceRuntime(this, manifest_.get(), true, init_done_cb, crash_cb);
360 main_subprocess_.set_service_runtime(service_runtime);
361 PLUGIN_PRINTF(("Plugin::LoadNaClModule (service_runtime=%p)\n",
362 static_cast<void*>(service_runtime)));
363 if (NULL == service_runtime) {
364 error_info.SetReport(
366 "sel_ldr init failure " + main_subprocess_.description());
367 ReportLoadError(error_info);
371 pp::CompletionCallback callback = callback_factory_.NewCallback(
372 &Plugin::LoadNexeAndStart, scoped_wrapper.release(), service_runtime,
374 StartSelLdrOnMainThread(
375 static_cast<int32_t>(PP_OK), service_runtime, params, callback);
378 void Plugin::LoadNexeAndStart(int32_t pp_error,
379 nacl::DescWrapper* wrapper,
380 ServiceRuntime* service_runtime,
381 const pp::CompletionCallback& crash_cb) {
382 nacl::scoped_ptr<nacl::DescWrapper> scoped_wrapper(wrapper);
383 if (pp_error != PP_OK)
386 // Now actually load the nexe, which can happen on a background thread.
387 bool nexe_loaded = service_runtime->LoadNexeAndStart(wrapper, crash_cb);
388 PLUGIN_PRINTF(("Plugin::LoadNaClModule (nexe_loaded=%d)\n",
391 PLUGIN_PRINTF(("Plugin::LoadNaClModule (%s)\n",
392 main_subprocess_.detailed_description().c_str()));
396 bool Plugin::LoadNaClModuleContinuationIntern(ErrorInfo* error_info) {
397 if (!main_subprocess_.StartSrpcServices()) {
398 // The NaCl process probably crashed. On Linux, a crash causes this error,
399 // while on other platforms, the error is detected below, when we attempt to
400 // start the proxy. Report a module initialization error here, to make it
401 // less confusing for developers.
402 NaClLog(LOG_ERROR, "LoadNaClModuleContinuationIntern: "
403 "StartSrpcServices failed\n");
404 error_info->SetReport(ERROR_START_PROXY_MODULE,
405 "could not initialize module.");
408 PP_ExternalPluginResult ipc_result =
409 nacl_interface_->StartPpapiProxy(pp_instance());
410 if (ipc_result == PP_EXTERNAL_PLUGIN_OK) {
411 // Log the amound of time that has passed between the trusted plugin being
412 // initialized and the untrusted plugin being initialized. This is
413 // (roughly) the cost of using NaCl, in terms of startup time.
414 HistogramStartupTimeMedium(
415 "NaCl.Perf.StartupTime.NaClOverhead",
416 static_cast<float>(NaClGetTimeOfDayMicroseconds() - init_time_)
417 / NACL_MICROS_PER_MILLI);
418 } else if (ipc_result == PP_EXTERNAL_PLUGIN_ERROR_MODULE) {
419 NaClLog(LOG_ERROR, "LoadNaClModuleContinuationIntern: "
420 "Got PP_EXTERNAL_PLUGIN_ERROR_MODULE\n");
421 error_info->SetReport(ERROR_START_PROXY_MODULE,
422 "could not initialize module.");
424 } else if (ipc_result == PP_EXTERNAL_PLUGIN_ERROR_INSTANCE) {
425 error_info->SetReport(ERROR_START_PROXY_INSTANCE,
426 "could not create instance.");
429 PLUGIN_PRINTF(("Plugin::LoadNaClModule (%s)\n",
430 main_subprocess_.detailed_description().c_str()));
434 NaClSubprocess* Plugin::LoadHelperNaClModule(nacl::DescWrapper* wrapper,
435 const Manifest* manifest,
436 ErrorInfo* error_info) {
437 nacl::scoped_ptr<NaClSubprocess> nacl_subprocess(
438 new NaClSubprocess("helper module", NULL, NULL));
439 if (NULL == nacl_subprocess.get()) {
440 error_info->SetReport(ERROR_SEL_LDR_INIT,
441 "unable to allocate helper subprocess.");
445 // Do not report UMA stats for translator-related nexes.
446 // TODO(sehr): define new UMA stats for translator related nexe events.
447 // NOTE: The PNaCl translator nexes are not built to use the IRT. This is
448 // done to save on address space and swap space.
449 // TODO(jvoung): See if we still need the uses_ppapi variable, now that
450 // LaunchSelLdr always happens on the main thread.
451 SelLdrStartParams params(manifest_base_url(),
452 false /* uses_irt */,
453 false /* uses_ppapi */,
454 enable_dev_interfaces_,
455 false /* enable_dyncode_syscalls */,
456 false /* enable_exception_handling */,
457 true /* enable_crash_throttling */);
458 if (!LoadNaClModuleFromBackgroundThread(wrapper, nacl_subprocess.get(),
462 // We need not wait for the init_done callback. We can block
463 // here in StartSrpcServices, since helper NaCl modules
464 // are spawned from a private thread.
466 // TODO(bsy): if helper module crashes, we should abort.
467 // crash_cb is not used here, so we are relying on crashes
468 // being detected in StartSrpcServices or later.
470 // NB: More refactoring might be needed, however, if helper
471 // NaCl modules have their own manifest. Currently the
472 // manifest is a per-plugin-instance object, not a per
473 // NaClSubprocess object.
474 if (!nacl_subprocess->StartSrpcServices()) {
475 error_info->SetReport(ERROR_SRPC_CONNECTION_FAIL,
476 "SRPC connection failure for " +
477 nacl_subprocess->description());
481 PLUGIN_PRINTF(("Plugin::LoadHelperNaClModule (%s)\n",
482 nacl_subprocess.get()->detailed_description().c_str()));
484 return nacl_subprocess.release();
487 std::string Plugin::LookupArgument(const std::string& key) const {
488 std::map<std::string, std::string>::const_iterator it = args_.find(key);
489 if (it != args_.end())
491 return std::string();
494 const char* const Plugin::kNaClMIMEType = "application/x-nacl";
495 const char* const Plugin::kPnaclMIMEType = "application/x-pnacl";
497 bool Plugin::NexeIsContentHandler() const {
498 // Tests if the MIME type is not a NaCl MIME type.
499 // If the MIME type is foreign, then this NEXE is being used as a content
500 // type handler rather than directly by an HTML document.
502 !mime_type().empty() &&
503 mime_type() != kNaClMIMEType &&
504 mime_type() != kPnaclMIMEType;
508 Plugin* Plugin::New(PP_Instance pp_instance) {
509 PLUGIN_PRINTF(("Plugin::New (pp_instance=%" NACL_PRId32 ")\n", pp_instance));
510 Plugin* plugin = new Plugin(pp_instance);
511 PLUGIN_PRINTF(("Plugin::New (plugin=%p)\n", static_cast<void*>(plugin)));
516 // All failures of this function will show up as "Missing Plugin-in", so
517 // there is no need to log to JS console that there was an initialization
518 // failure. Note that module loading functions will log their own errors.
519 bool Plugin::Init(uint32_t argc, const char* argn[], const char* argv[]) {
520 PLUGIN_PRINTF(("Plugin::Init (argc=%" NACL_PRIu32 ")\n", argc));
521 HistogramEnumerateOsArch(GetSandboxISA());
522 init_time_ = NaClGetTimeOfDayMicroseconds();
523 url_util_ = pp::URLUtil_Dev::Get();
524 if (url_util_ == NULL)
527 PLUGIN_PRINTF(("Plugin::Init (url_util_=%p)\n",
528 static_cast<const void*>(url_util_)));
530 bool status = EarlyInit(static_cast<int>(argc), argn, argv);
532 // Look for the developer attribute; if it's present, enable 'dev'
534 enable_dev_interfaces_ = args_.find(kDevAttribute) != args_.end();
536 mime_type_ = LookupArgument(kTypeAttribute);
537 std::transform(mime_type_.begin(), mime_type_.end(), mime_type_.begin(),
540 std::string manifest_url;
541 if (NexeIsContentHandler()) {
542 // For content handlers 'src' will be the URL for the content
543 // and 'nacl' will be the URL for the manifest.
544 manifest_url = LookupArgument(kNaClManifestAttribute);
545 // For content handlers the NEXE runs in the security context of the
546 // content it is rendering and the NEXE itself appears to be a
547 // cross-origin resource stored in a Chrome extension.
549 manifest_url = LookupArgument(kSrcManifestAttribute);
551 // Use the document URL as the base for resolving relative URLs to find the
552 // manifest. This takes into account the setting of <base> tags that
553 // precede the embed/object.
554 CHECK(url_util_ != NULL);
555 pp::Var base_var = url_util_->GetDocumentURL(this);
556 if (!base_var.is_string()) {
557 PLUGIN_PRINTF(("Plugin::Init (unable to find document url)\n"));
560 set_plugin_base_url(base_var.AsString());
561 if (manifest_url.empty()) {
562 // TODO(sehr,polina): this should be a hard error when scripting
563 // the src property is no longer allowed.
564 PLUGIN_PRINTF(("Plugin::Init:"
565 " WARNING: no 'src' property, so no manifest loaded.\n"));
566 if (args_.find(kNaClManifestAttribute) != args_.end()) {
567 PLUGIN_PRINTF(("Plugin::Init:"
568 " WARNING: 'nacl' property is incorrect. Use 'src'.\n"));
571 // Issue a GET for the manifest_url. The manifest file will be parsed to
572 // determine the nexe URL.
573 // Sets src property to full manifest URL.
574 RequestNaClManifest(manifest_url.c_str());
578 PLUGIN_PRINTF(("Plugin::Init (status=%d)\n", status));
582 Plugin::Plugin(PP_Instance pp_instance)
583 : pp::Instance(pp_instance),
584 main_subprocess_("main subprocess", NULL, NULL),
585 nexe_error_reported_(false),
586 wrapper_factory_(NULL),
587 enable_dev_interfaces_(false),
588 is_installed_(false),
592 time_of_last_progress_event_(0),
594 nacl_interface_(NULL),
595 uma_interface_(this) {
596 PLUGIN_PRINTF(("Plugin::Plugin (this=%p, pp_instance=%"
597 NACL_PRId32 ")\n", static_cast<void*>(this), pp_instance));
598 callback_factory_.Initialize(this);
599 nexe_downloader_.Initialize(this);
600 nacl_interface_ = GetNaClInterface();
601 CHECK(nacl_interface_ != NULL);
602 set_nacl_ready_state(UNSENT);
603 set_last_error_string("");
604 // We call set_exit_status() here to ensure that the 'exitStatus' property is
605 // set. This can only be called when nacl_interface_ is not NULL.
611 int64_t shutdown_start = NaClGetTimeOfDayMicroseconds();
613 PLUGIN_PRINTF(("Plugin::~Plugin (this=%p)\n",
614 static_cast<void*>(this)));
615 // Destroy the coordinator while the rest of the data is still there
616 pnacl_coordinator_.reset(NULL);
618 if (!nexe_error_reported()) {
620 "NaCl.ModuleUptime.Normal",
621 (shutdown_start - ready_time_) / NACL_MICROS_PER_MILLI);
624 for (std::map<nacl::string, NaClFileInfoAutoCloser*>::iterator it =
625 url_file_info_map_.begin();
626 it != url_file_info_map_.end();
630 url_downloaders_.erase(url_downloaders_.begin(), url_downloaders_.end());
632 // ShutDownSubprocesses shuts down the main subprocess, which shuts
633 // down the main ServiceRuntime object, which kills the subprocess.
634 // As a side effect of the subprocess being killed, the reverse
635 // services thread(s) will get EOF on the reverse channel(s), and
636 // the thread(s) will exit. In ServiceRuntime::Shutdown, we invoke
637 // ReverseService::WaitForServiceThreadsToExit(), so that there will
638 // not be an extent thread(s) hanging around. This means that the
639 // ~Plugin will block until this happens. This is a requirement,
640 // since the renderer should be free to unload the plugin code, and
641 // we cannot have threads running code that gets unloaded before
644 // By waiting for the threads here, we also ensure that the Plugin
645 // object and the subprocess and ServiceRuntime objects is not
646 // (fully) destroyed while the threads are running, so resources
647 // that are destroyed after ShutDownSubprocesses (below) are
648 // guaranteed to be live and valid for access from the service
651 // The main_subprocess object, which wraps the main service_runtime
652 // object, is dtor'd implicitly after the explicit code below runs,
653 // so the main service runtime object will not have been dtor'd,
654 // though the Shutdown method may have been called, during the
655 // lifetime of the service threads.
656 ShutDownSubprocesses();
658 delete wrapper_factory_;
661 "NaCl.Perf.ShutdownTime.Total",
662 (NaClGetTimeOfDayMicroseconds() - shutdown_start)
663 / NACL_MICROS_PER_MILLI);
665 PLUGIN_PRINTF(("Plugin::~Plugin (this=%p, return)\n",
666 static_cast<void*>(this)));
669 bool Plugin::HandleDocumentLoad(const pp::URLLoader& url_loader) {
670 PLUGIN_PRINTF(("Plugin::HandleDocumentLoad (this=%p)\n",
671 static_cast<void*>(this)));
672 // We don't know if the plugin will handle the document load, but return
673 // true in order to give it a chance to respond once the proxy is started.
677 void Plugin::HistogramStartupTimeSmall(const std::string& name, float dt) {
678 if (nexe_size_ > 0) {
679 float size_in_MB = static_cast<float>(nexe_size_) / (1024.f * 1024.f);
680 HistogramTimeSmall(name, static_cast<int64_t>(dt));
681 HistogramTimeSmall(name + "PerMB", static_cast<int64_t>(dt / size_in_MB));
685 void Plugin::HistogramStartupTimeMedium(const std::string& name, float dt) {
686 if (nexe_size_ > 0) {
687 float size_in_MB = static_cast<float>(nexe_size_) / (1024.f * 1024.f);
688 HistogramTimeMedium(name, static_cast<int64_t>(dt));
689 HistogramTimeMedium(name + "PerMB", static_cast<int64_t>(dt / size_in_MB));
693 void Plugin::NexeFileDidOpen(int32_t pp_error) {
694 PLUGIN_PRINTF(("Plugin::NexeFileDidOpen (pp_error=%" NACL_PRId32 ")\n",
696 NaClFileInfo tmp_info(nexe_downloader_.GetFileInfo());
697 NaClFileInfoAutoCloser info(&tmp_info);
698 PLUGIN_PRINTF(("Plugin::NexeFileDidOpen (file_desc=%" NACL_PRId32 ")\n",
700 HistogramHTTPStatusCode(
702 "NaCl.HttpStatusCodeClass.Nexe.InstalledApp" :
703 "NaCl.HttpStatusCodeClass.Nexe.NotInstalledApp",
704 nexe_downloader_.status_code());
705 ErrorInfo error_info;
706 if (pp_error != PP_OK || info.get_desc() == NACL_NO_FILE_DESC) {
707 if (pp_error == PP_ERROR_ABORTED) {
709 } else if (pp_error == PP_ERROR_NOACCESS) {
710 error_info.SetReport(ERROR_NEXE_NOACCESS_URL,
711 "access to nexe url was denied.");
712 ReportLoadError(error_info);
714 error_info.SetReport(ERROR_NEXE_LOAD_URL, "could not load nexe url.");
715 ReportLoadError(error_info);
719 int32_t file_desc_ok_to_close = DUP(info.get_desc());
720 if (file_desc_ok_to_close == NACL_NO_FILE_DESC) {
721 error_info.SetReport(ERROR_NEXE_FH_DUP,
722 "could not duplicate loaded file handle.");
723 ReportLoadError(error_info);
726 struct stat stat_buf;
727 if (0 != fstat(file_desc_ok_to_close, &stat_buf)) {
728 CLOSE(file_desc_ok_to_close);
729 error_info.SetReport(ERROR_NEXE_STAT, "could not stat nexe file.");
730 ReportLoadError(error_info);
733 size_t nexe_bytes_read = static_cast<size_t>(stat_buf.st_size);
735 nexe_size_ = nexe_bytes_read;
736 HistogramSizeKB("NaCl.Perf.Size.Nexe",
737 static_cast<int32_t>(nexe_size_ / 1024));
738 HistogramStartupTimeMedium(
739 "NaCl.Perf.StartupTime.NexeDownload",
740 static_cast<float>(nexe_downloader_.TimeSinceOpenMilliseconds()));
742 // Inform JavaScript that we successfully downloaded the nacl module.
743 EnqueueProgressEvent(PP_NACL_EVENT_PROGRESS,
744 nexe_downloader_.url_to_open(),
745 LENGTH_IS_COMPUTABLE,
749 load_start_ = NaClGetTimeOfDayMicroseconds();
750 nacl::scoped_ptr<nacl::DescWrapper>
751 wrapper(wrapper_factory()->MakeFileDesc(file_desc_ok_to_close, O_RDONLY));
752 NaClLog(4, "NexeFileDidOpen: invoking LoadNaClModule\n");
755 true, /* enable_dyncode_syscalls */
756 true, /* enable_exception_handling */
757 false, /* enable_crash_throttling */
758 callback_factory_.NewCallback(&Plugin::NexeFileDidOpenContinuation),
759 callback_factory_.NewCallback(&Plugin::NexeDidCrash));
762 void Plugin::NexeFileDidOpenContinuation(int32_t pp_error) {
763 ErrorInfo error_info;
766 UNREFERENCED_PARAMETER(pp_error);
767 NaClLog(4, "Entered NexeFileDidOpenContinuation\n");
768 NaClLog(4, "NexeFileDidOpenContinuation: invoking"
769 " LoadNaClModuleContinuationIntern\n");
770 was_successful = LoadNaClModuleContinuationIntern(&error_info);
771 if (was_successful) {
772 NaClLog(4, "NexeFileDidOpenContinuation: success;"
773 " setting histograms\n");
774 ready_time_ = NaClGetTimeOfDayMicroseconds();
775 HistogramStartupTimeSmall(
776 "NaCl.Perf.StartupTime.LoadModule",
777 static_cast<float>(ready_time_ - load_start_) / NACL_MICROS_PER_MILLI);
778 HistogramStartupTimeMedium(
779 "NaCl.Perf.StartupTime.Total",
780 static_cast<float>(ready_time_ - init_time_) / NACL_MICROS_PER_MILLI);
782 ReportLoadSuccess(LENGTH_IS_COMPUTABLE, nexe_size_, nexe_size_);
784 NaClLog(4, "NexeFileDidOpenContinuation: failed.");
785 ReportLoadError(error_info);
787 NaClLog(4, "Leaving NexeFileDidOpenContinuation\n");
790 static void LogLineToConsole(Plugin* plugin, const nacl::string& one_line) {
791 PLUGIN_PRINTF(("LogLineToConsole: %s\n",
793 plugin->AddToConsole(one_line);
796 void Plugin::CopyCrashLogToJsConsole() {
797 nacl::string fatal_msg(main_service_runtime()->GetCrashLogOutput());
801 PLUGIN_PRINTF(("Plugin::CopyCrashLogToJsConsole: got %" NACL_PRIuS " bytes\n",
803 while (nacl::string::npos != (ix_end = fatal_msg.find('\n', ix_start))) {
804 LogLineToConsole(this, fatal_msg.substr(ix_start, ix_end - ix_start));
805 ix_start = ix_end + 1;
807 if (ix_start != fatal_msg.size()) {
808 LogLineToConsole(this, fatal_msg.substr(ix_start));
812 void Plugin::NexeDidCrash(int32_t pp_error) {
813 PLUGIN_PRINTF(("Plugin::NexeDidCrash (pp_error=%" NACL_PRId32 ")\n",
815 if (pp_error != PP_OK) {
816 PLUGIN_PRINTF(("Plugin::NexeDidCrash: CallOnMainThread callback with"
817 " non-PP_OK arg -- SHOULD NOT HAPPEN\n"));
819 PLUGIN_PRINTF(("Plugin::NexeDidCrash: crash event!\n"));
820 if (-1 != exit_status()) {
821 // The NaCl module voluntarily exited. However, this is still a
822 // crash from the point of view of Pepper, since PPAPI plugins are
823 // event handlers and should never exit.
824 PLUGIN_PRINTF((("Plugin::NexeDidCrash: nexe exited with status %d"
825 " so this is a \"controlled crash\".\n"),
828 // If the crash occurs during load, we just want to report an error
829 // that fits into our load progress event grammar. If the crash
830 // occurs after loaded/loadend, then we use ReportDeadNexe to send a
832 if (nexe_error_reported()) {
833 PLUGIN_PRINTF(("Plugin::NexeDidCrash: error already reported;"
836 if (nacl_ready_state_ == DONE) {
839 ErrorInfo error_info;
840 // The error is not quite right. In particular, the crash
841 // reported by this path could be due to NaCl application
842 // crashes that occur after the PPAPI proxy has started.
843 error_info.SetReport(ERROR_START_PROXY_CRASH,
844 "Nexe crashed during startup");
845 ReportLoadError(error_info);
849 // In all cases, try to grab the crash log. The first error
850 // reported may have come from the start_module RPC reply indicating
851 // a validation error or something similar, which wouldn't grab the
852 // crash log. In the event that this is called twice, the second
853 // invocation will just be a no-op, since all the crash log will
854 // have been received and we'll just get an EOF indication.
855 CopyCrashLogToJsConsole();
858 void Plugin::BitcodeDidTranslate(int32_t pp_error) {
859 PLUGIN_PRINTF(("Plugin::BitcodeDidTranslate (pp_error=%" NACL_PRId32 ")\n",
861 if (pp_error != PP_OK) {
862 // Error should have been reported by pnacl. Just return.
863 PLUGIN_PRINTF(("Plugin::BitcodeDidTranslate error in Pnacl\n"));
867 // Inform JavaScript that we successfully translated the bitcode to a nexe.
868 nacl::scoped_ptr<nacl::DescWrapper>
869 wrapper(pnacl_coordinator_.get()->ReleaseTranslatedFD());
872 false, /* enable_dyncode_syscalls */
873 false, /* enable_exception_handling */
874 true, /* enable_crash_throttling */
875 callback_factory_.NewCallback(&Plugin::BitcodeDidTranslateContinuation),
876 callback_factory_.NewCallback(&Plugin::NexeDidCrash));
879 void Plugin::BitcodeDidTranslateContinuation(int32_t pp_error) {
880 ErrorInfo error_info;
881 bool was_successful = LoadNaClModuleContinuationIntern(&error_info);
883 NaClLog(4, "Entered BitcodeDidTranslateContinuation\n");
884 UNREFERENCED_PARAMETER(pp_error);
885 if (was_successful) {
888 pnacl_coordinator_->GetCurrentProgress(&loaded, &total);
889 ReportLoadSuccess(LENGTH_IS_COMPUTABLE, loaded, total);
891 ReportLoadError(error_info);
895 void Plugin::ReportDeadNexe() {
896 PLUGIN_PRINTF(("Plugin::ReportDeadNexe\n"));
898 if (nacl_ready_state_ == DONE && !nexe_error_reported()) { // After loadEnd.
899 int64_t crash_time = NaClGetTimeOfDayMicroseconds();
900 // Crashes will be more likely near startup, so use a medium histogram
901 // instead of a large one.
903 "NaCl.ModuleUptime.Crash",
904 (crash_time - ready_time_) / NACL_MICROS_PER_MILLI);
906 nacl::string message = nacl::string("NaCl module crashed");
907 set_last_error_string(message);
908 AddToConsole(message);
910 EnqueueProgressEvent(PP_NACL_EVENT_CRASH);
911 set_nexe_error_reported(true);
913 // else ReportLoadError() and ReportAbortError() will be used by loading code
914 // to provide error handling.
916 // NOTE: not all crashes during load will make it here.
917 // Those in BrowserPpp::InitializeModule and creation of PPP interfaces
918 // will just get reported back as PP_ERROR_FAILED.
921 void Plugin::NaClManifestBufferReady(int32_t pp_error) {
922 PLUGIN_PRINTF(("Plugin::NaClManifestBufferReady (pp_error=%"
923 NACL_PRId32 ")\n", pp_error));
924 ErrorInfo error_info;
925 if (pp_error != PP_OK) {
926 if (pp_error == PP_ERROR_ABORTED) {
929 error_info.SetReport(ERROR_MANIFEST_LOAD_URL,
930 "could not load manifest url.");
931 ReportLoadError(error_info);
936 const std::deque<char>& buffer = nexe_downloader_.buffer();
937 size_t buffer_size = buffer.size();
938 if (buffer_size > kNaClManifestMaxFileBytes) {
939 error_info.SetReport(ERROR_MANIFEST_TOO_LARGE,
940 "manifest file too large.");
941 ReportLoadError(error_info);
944 nacl::scoped_array<char> json_buffer(new char[buffer_size + 1]);
945 if (json_buffer == NULL) {
946 error_info.SetReport(ERROR_MANIFEST_MEMORY_ALLOC,
947 "could not allocate manifest memory.");
948 ReportLoadError(error_info);
951 std::copy(buffer.begin(), buffer.begin() + buffer_size, &json_buffer[0]);
952 json_buffer[buffer_size] = '\0';
954 ProcessNaClManifest(json_buffer.get());
957 void Plugin::NaClManifestFileDidOpen(int32_t pp_error) {
958 PLUGIN_PRINTF(("Plugin::NaClManifestFileDidOpen (pp_error=%"
959 NACL_PRId32 ")\n", pp_error));
960 HistogramTimeSmall("NaCl.Perf.StartupTime.ManifestDownload",
961 nexe_downloader_.TimeSinceOpenMilliseconds());
962 HistogramHTTPStatusCode(
964 "NaCl.HttpStatusCodeClass.Manifest.InstalledApp" :
965 "NaCl.HttpStatusCodeClass.Manifest.NotInstalledApp",
966 nexe_downloader_.status_code());
967 ErrorInfo error_info;
968 NaClFileInfo tmp_info(nexe_downloader_.GetFileInfo());
969 NaClFileInfoAutoCloser info(&tmp_info);
970 PLUGIN_PRINTF(("Plugin::NaClManifestFileDidOpen (file_desc=%"
971 NACL_PRId32 ")\n", info.get_desc()));
972 if (pp_error != PP_OK || info.get_desc() == NACL_NO_FILE_DESC) {
973 if (pp_error == PP_ERROR_ABORTED) {
975 } else if (pp_error == PP_ERROR_NOACCESS) {
976 error_info.SetReport(ERROR_MANIFEST_NOACCESS_URL,
977 "access to manifest url was denied.");
978 ReportLoadError(error_info);
980 error_info.SetReport(ERROR_MANIFEST_LOAD_URL,
981 "could not load manifest url.");
982 ReportLoadError(error_info);
986 // SlurpFile closes the file descriptor after reading (or on error).
987 // Duplicate our file descriptor since it will be handled by the browser.
988 int dup_file_desc = DUP(info.get_desc());
989 nacl::string json_buffer;
990 file_utils::StatusCode status = file_utils::SlurpFile(
991 dup_file_desc, json_buffer, kNaClManifestMaxFileBytes);
993 if (status != file_utils::PLUGIN_FILE_SUCCESS) {
995 case file_utils::PLUGIN_FILE_SUCCESS:
998 case file_utils::PLUGIN_FILE_ERROR_MEM_ALLOC:
999 error_info.SetReport(ERROR_MANIFEST_MEMORY_ALLOC,
1000 "could not allocate manifest memory.");
1002 case file_utils::PLUGIN_FILE_ERROR_OPEN:
1003 error_info.SetReport(ERROR_MANIFEST_OPEN,
1004 "could not open manifest file.");
1006 case file_utils::PLUGIN_FILE_ERROR_FILE_TOO_LARGE:
1007 error_info.SetReport(ERROR_MANIFEST_TOO_LARGE,
1008 "manifest file too large.");
1010 case file_utils::PLUGIN_FILE_ERROR_STAT:
1011 error_info.SetReport(ERROR_MANIFEST_STAT,
1012 "could not stat manifest file.");
1014 case file_utils::PLUGIN_FILE_ERROR_READ:
1015 error_info.SetReport(ERROR_MANIFEST_READ,
1016 "could not read manifest file.");
1019 ReportLoadError(error_info);
1023 ProcessNaClManifest(json_buffer);
1026 void Plugin::ProcessNaClManifest(const nacl::string& manifest_json) {
1027 HistogramSizeKB("NaCl.Perf.Size.Manifest",
1028 static_cast<int32_t>(manifest_json.length() / 1024));
1029 nacl::string program_url;
1030 PnaclOptions pnacl_options;
1031 ErrorInfo error_info;
1032 if (!SetManifestObject(manifest_json, &error_info)) {
1033 ReportLoadError(error_info);
1037 if (manifest_->GetProgramURL(&program_url, &pnacl_options, &error_info)) {
1038 is_installed_ = GetUrlScheme(program_url) == SCHEME_CHROME_EXTENSION;
1039 set_nacl_ready_state(LOADING);
1040 // Inform JavaScript that we found a nexe URL to load.
1041 EnqueueProgressEvent(PP_NACL_EVENT_PROGRESS);
1042 if (pnacl_options.translate()) {
1043 pp::CompletionCallback translate_callback =
1044 callback_factory_.NewCallback(&Plugin::BitcodeDidTranslate);
1045 // Will always call the callback on success or failure.
1046 pnacl_coordinator_.reset(
1047 PnaclCoordinator::BitcodeToNative(this,
1050 translate_callback));
1053 // Try the fast path first. This will only block if the file is installed.
1054 if (OpenURLFast(program_url, &nexe_downloader_)) {
1055 NexeFileDidOpen(PP_OK);
1057 pp::CompletionCallback open_callback =
1058 callback_factory_.NewCallback(&Plugin::NexeFileDidOpen);
1059 // Will always call the callback on success or failure.
1061 nexe_downloader_.Open(program_url,
1065 &UpdateDownloadProgress));
1070 // Failed to select the program and/or the translator.
1071 ReportLoadError(error_info);
1074 void Plugin::RequestNaClManifest(const nacl::string& url) {
1075 PLUGIN_PRINTF(("Plugin::RequestNaClManifest (url='%s')\n", url.c_str()));
1076 PLUGIN_PRINTF(("Plugin::RequestNaClManifest (plugin base url='%s')\n",
1077 plugin_base_url().c_str()));
1078 // The full URL of the manifest file is relative to the base url.
1079 CHECK(url_util_ != NULL);
1080 pp::Var nmf_resolved_url =
1081 url_util_->ResolveRelativeToURL(plugin_base_url(), pp::Var(url));
1082 if (!nmf_resolved_url.is_string()) {
1083 ErrorInfo error_info;
1084 error_info.SetReport(
1085 ERROR_MANIFEST_RESOLVE_URL,
1086 nacl::string("could not resolve URL \"") + url.c_str() +
1087 "\" relative to \"" + plugin_base_url().c_str() + "\".");
1088 ReportLoadError(error_info);
1091 PLUGIN_PRINTF(("Plugin::RequestNaClManifest (resolved url='%s')\n",
1092 nmf_resolved_url.AsString().c_str()));
1093 is_installed_ = GetUrlScheme(nmf_resolved_url.AsString()) ==
1094 SCHEME_CHROME_EXTENSION;
1095 set_manifest_base_url(nmf_resolved_url.AsString());
1096 // Inform JavaScript that a load is starting.
1097 set_nacl_ready_state(OPENED);
1098 EnqueueProgressEvent(PP_NACL_EVENT_LOADSTART);
1099 bool is_data_uri = GetUrlScheme(nmf_resolved_url.AsString()) == SCHEME_DATA;
1100 HistogramEnumerateManifestIsDataURI(static_cast<int>(is_data_uri));
1102 pp::CompletionCallback open_callback =
1103 callback_factory_.NewCallback(&Plugin::NaClManifestBufferReady);
1104 // Will always call the callback on success or failure.
1105 CHECK(nexe_downloader_.Open(nmf_resolved_url.AsString(),
1111 pp::CompletionCallback open_callback =
1112 callback_factory_.NewCallback(&Plugin::NaClManifestFileDidOpen);
1113 // Will always call the callback on success or failure.
1114 CHECK(nexe_downloader_.Open(nmf_resolved_url.AsString(),
1123 bool Plugin::SetManifestObject(const nacl::string& manifest_json,
1124 ErrorInfo* error_info) {
1125 PLUGIN_PRINTF(("Plugin::SetManifestObject(): manifest_json='%s'.\n",
1126 manifest_json.c_str()));
1127 if (error_info == NULL)
1129 // Determine whether lookups should use portable (i.e., pnacl versions)
1130 // rather than platform-specific files.
1131 bool is_pnacl = (mime_type() == kPnaclMIMEType);
1132 nacl::scoped_ptr<JsonManifest> json_manifest(
1133 new JsonManifest(url_util_,
1134 manifest_base_url(),
1135 (is_pnacl ? kPortableISA : GetSandboxISA())));
1136 if (!json_manifest->Init(manifest_json, error_info)) {
1139 manifest_.reset(json_manifest.release());
1143 void Plugin::UrlDidOpenForStreamAsFile(int32_t pp_error,
1144 FileDownloader*& url_downloader,
1145 PP_CompletionCallback callback) {
1146 PLUGIN_PRINTF(("Plugin::UrlDidOpen (pp_error=%" NACL_PRId32
1147 ", url_downloader=%p)\n", pp_error,
1148 static_cast<void*>(url_downloader)));
1149 url_downloaders_.erase(url_downloader);
1150 nacl::scoped_ptr<FileDownloader> scoped_url_downloader(url_downloader);
1151 NaClFileInfo tmp_info(scoped_url_downloader->GetFileInfo());
1152 NaClFileInfoAutoCloser *info = new NaClFileInfoAutoCloser(&tmp_info);
1154 if (pp_error != PP_OK) {
1155 PP_RunCompletionCallback(&callback, pp_error);
1157 } else if (info->get_desc() > NACL_NO_FILE_DESC) {
1158 std::map<nacl::string, NaClFileInfoAutoCloser*>::iterator it =
1159 url_file_info_map_.find(url_downloader->url_to_open());
1160 if (it != url_file_info_map_.end()) {
1163 url_file_info_map_[url_downloader->url_to_open()] = info;
1164 PP_RunCompletionCallback(&callback, PP_OK);
1166 PP_RunCompletionCallback(&callback, PP_ERROR_FAILED);
1171 struct NaClFileInfo Plugin::GetFileInfo(const nacl::string& url) {
1172 struct NaClFileInfo info;
1173 memset(&info, 0, sizeof(info));
1174 std::map<nacl::string, NaClFileInfoAutoCloser*>::iterator it =
1175 url_file_info_map_.find(url);
1176 if (it != url_file_info_map_.end()) {
1177 info = it->second->get();
1178 info.desc = DUP(info.desc);
1185 bool Plugin::StreamAsFile(const nacl::string& url,
1186 PP_CompletionCallback callback) {
1187 PLUGIN_PRINTF(("Plugin::StreamAsFile (url='%s')\n", url.c_str()));
1188 FileDownloader* downloader = new FileDownloader();
1189 downloader->Initialize(this);
1190 url_downloaders_.insert(downloader);
1191 // Untrusted loads are always relative to the page's origin.
1192 CHECK(url_util_ != NULL);
1193 pp::Var resolved_url =
1194 url_util_->ResolveRelativeToURL(pp::Var(plugin_base_url()), url);
1195 if (!resolved_url.is_string()) {
1196 PLUGIN_PRINTF(("Plugin::StreamAsFile: "
1197 "could not resolve url \"%s\" relative to plugin \"%s\".",
1199 plugin_base_url().c_str()));
1203 // Try the fast path first. This will only block if the file is installed.
1204 if (OpenURLFast(url, downloader)) {
1205 UrlDidOpenForStreamAsFile(PP_OK, downloader, callback);
1209 pp::CompletionCallback open_callback = callback_factory_.NewCallback(
1210 &Plugin::UrlDidOpenForStreamAsFile, downloader, callback);
1211 // If true, will always call the callback on success or failure.
1212 return downloader->Open(url,
1216 &UpdateDownloadProgress);
1220 void Plugin::ReportLoadSuccess(LengthComputable length_computable,
1221 uint64_t loaded_bytes,
1222 uint64_t total_bytes) {
1223 // Set the readyState attribute to indicate loaded.
1224 set_nacl_ready_state(DONE);
1225 // Inform JavaScript that loading was successful and is complete.
1226 const nacl::string& url = nexe_downloader_.url_to_open();
1227 EnqueueProgressEvent(
1228 PP_NACL_EVENT_LOAD, url, length_computable, loaded_bytes, total_bytes);
1229 EnqueueProgressEvent(
1230 PP_NACL_EVENT_LOADEND, url, length_computable, loaded_bytes, total_bytes);
1233 HistogramEnumerateLoadStatus(ERROR_LOAD_SUCCESS, is_installed_);
1237 void Plugin::ReportLoadError(const ErrorInfo& error_info) {
1238 PLUGIN_PRINTF(("Plugin::ReportLoadError (error='%s')\n",
1239 error_info.message().c_str()));
1240 // For errors the user (and not just the developer) should know about,
1241 // report them to the renderer so the browser can display a message.
1242 if (error_info.error_code() == ERROR_MANIFEST_PROGRAM_MISSING_ARCH) {
1243 // A special case: the manifest may otherwise be valid but is missing
1244 // a program/file compatible with the user's sandbox.
1245 nacl_interface()->ReportNaClError(pp_instance(),
1246 PP_NACL_MANIFEST_MISSING_ARCH);
1249 // Set the readyState attribute to indicate we need to start over.
1250 set_nacl_ready_state(DONE);
1251 set_nexe_error_reported(true);
1252 // Report an error in lastError and on the JavaScript console.
1253 nacl::string message = nacl::string("NaCl module load failed: ") +
1254 error_info.message();
1255 set_last_error_string(message);
1256 AddToConsole(nacl::string("NaCl module load failed: ") +
1257 error_info.console_message());
1258 // Inform JavaScript that loading encountered an error and is complete.
1259 EnqueueProgressEvent(PP_NACL_EVENT_ERROR);
1260 EnqueueProgressEvent(PP_NACL_EVENT_LOADEND);
1263 HistogramEnumerateLoadStatus(error_info.error_code(), is_installed_);
1267 void Plugin::ReportLoadAbort() {
1268 PLUGIN_PRINTF(("Plugin::ReportLoadAbort\n"));
1269 // Set the readyState attribute to indicate we need to start over.
1270 set_nacl_ready_state(DONE);
1271 set_nexe_error_reported(true);
1272 // Report an error in lastError and on the JavaScript console.
1273 nacl::string error_string("NaCl module load failed: user aborted");
1274 set_last_error_string(error_string);
1275 AddToConsole(error_string);
1276 // Inform JavaScript that loading was aborted and is complete.
1277 EnqueueProgressEvent(PP_NACL_EVENT_ABORT);
1278 EnqueueProgressEvent(PP_NACL_EVENT_LOADEND);
1281 HistogramEnumerateLoadStatus(ERROR_LOAD_ABORTED, is_installed_);
1284 void Plugin::UpdateDownloadProgress(
1285 PP_Instance pp_instance,
1286 PP_Resource pp_resource,
1287 int64_t /*bytes_sent*/,
1288 int64_t /*total_bytes_to_be_sent*/,
1289 int64_t bytes_received,
1290 int64_t total_bytes_to_be_received) {
1291 Instance* instance = pp::Module::Get()->InstanceForPPInstance(pp_instance);
1292 if (instance != NULL) {
1293 Plugin* plugin = static_cast<Plugin*>(instance);
1294 // Rate limit progress events to a maximum of 100 per second.
1295 int64_t time = NaClGetTimeOfDayMicroseconds();
1296 int64_t elapsed = time - plugin->time_of_last_progress_event_;
1297 const int64_t kTenMilliseconds = 10000;
1298 if (elapsed > kTenMilliseconds) {
1299 plugin->time_of_last_progress_event_ = time;
1301 // Find the URL loader that sent this notification.
1302 const FileDownloader* file_downloader =
1303 plugin->FindFileDownloader(pp_resource);
1304 // If not a streamed file, it must be the .nexe loader.
1305 if (file_downloader == NULL)
1306 file_downloader = &plugin->nexe_downloader_;
1307 nacl::string url = file_downloader->url_to_open();
1308 LengthComputable length_computable = (total_bytes_to_be_received >= 0) ?
1309 LENGTH_IS_COMPUTABLE : LENGTH_IS_NOT_COMPUTABLE;
1311 plugin->EnqueueProgressEvent(PP_NACL_EVENT_PROGRESS,
1315 total_bytes_to_be_received);
1320 const FileDownloader* Plugin::FindFileDownloader(
1321 PP_Resource url_loader) const {
1322 const FileDownloader* file_downloader = NULL;
1323 if (url_loader == nexe_downloader_.url_loader()) {
1324 file_downloader = &nexe_downloader_;
1326 std::set<FileDownloader*>::const_iterator it = url_downloaders_.begin();
1327 while (it != url_downloaders_.end()) {
1328 if (url_loader == (*it)->url_loader()) {
1329 file_downloader = (*it);
1335 return file_downloader;
1338 void Plugin::ReportSelLdrLoadStatus(int status) {
1339 HistogramEnumerateSelLdrLoadStatus(static_cast<NaClErrorCode>(status),
1343 void Plugin::EnqueueProgressEvent(PP_NaClEventType event_type) {
1344 EnqueueProgressEvent(event_type,
1346 Plugin::LENGTH_IS_NOT_COMPUTABLE,
1347 Plugin::kUnknownBytes,
1348 Plugin::kUnknownBytes);
1351 void Plugin::EnqueueProgressEvent(PP_NaClEventType event_type,
1352 const nacl::string& url,
1353 LengthComputable length_computable,
1354 uint64_t loaded_bytes,
1355 uint64_t total_bytes) {
1356 PLUGIN_PRINTF(("Plugin::EnqueueProgressEvent ("
1357 "event_type='%d', url='%s', length_computable=%d, "
1358 "loaded=%" NACL_PRIu64 ", total=%" NACL_PRIu64 ")\n",
1359 static_cast<int>(event_type),
1361 static_cast<int>(length_computable),
1365 nacl_interface_->DispatchEvent(
1369 length_computable == LENGTH_IS_COMPUTABLE ? PP_TRUE : PP_FALSE,
1374 bool Plugin::OpenURLFast(const nacl::string& url,
1375 FileDownloader* downloader) {
1376 // Fast path only works for installed file URLs.
1377 if (GetUrlScheme(url) != SCHEME_CHROME_EXTENSION)
1379 // IMPORTANT: Make sure the document can request the given URL. If we don't
1380 // check, a malicious app could probe the extension system. This enforces a
1381 // same-origin policy which prevents the app from requesting resources from
1383 if (!DocumentCanRequest(url))
1386 uint64_t file_token_lo = 0;
1387 uint64_t file_token_hi = 0;
1388 PP_FileHandle file_handle =
1389 nacl_interface()->OpenNaClExecutable(pp_instance(),
1391 &file_token_lo, &file_token_hi);
1392 // We shouldn't hit this if the file URL is in an installed app.
1393 if (file_handle == PP_kInvalidFileHandle)
1396 // FileDownloader takes ownership of the file handle.
1397 downloader->OpenFast(url, file_handle, file_token_lo, file_token_hi);
1401 UrlSchemeType Plugin::GetUrlScheme(const std::string& url) {
1402 CHECK(url_util_ != NULL);
1403 PP_URLComponents_Dev comps;
1404 pp::Var canonicalized =
1405 url_util_->Canonicalize(pp::Var(url), &comps);
1407 if (canonicalized.is_null() ||
1408 (comps.scheme.begin == 0 && comps.scheme.len == -1)) {
1409 // |url| was an invalid URL or has no scheme.
1410 return SCHEME_OTHER;
1413 CHECK(comps.scheme.begin <
1414 static_cast<int>(canonicalized.AsString().size()));
1415 CHECK(comps.scheme.begin + comps.scheme.len <
1416 static_cast<int>(canonicalized.AsString().size()));
1418 std::string scheme = canonicalized.AsString().substr(comps.scheme.begin,
1420 if (scheme == kChromeExtensionUriScheme)
1421 return SCHEME_CHROME_EXTENSION;
1422 if (scheme == kDataUriScheme)
1424 return SCHEME_OTHER;
1427 bool Plugin::DocumentCanRequest(const std::string& url) {
1428 CHECK(url_util_ != NULL);
1429 return url_util_->DocumentCanRequest(this, pp::Var(url));
1432 void Plugin::AddToConsole(const nacl::string& text) {
1433 pp::Module* module = pp::Module::Get();
1434 const PPB_Var* var_interface =
1435 static_cast<const PPB_Var*>(
1436 module->GetBrowserInterface(PPB_VAR_INTERFACE));
1437 nacl::string prefix_string("NativeClient");
1439 var_interface->VarFromUtf8(prefix_string.c_str(),
1440 static_cast<uint32_t>(prefix_string.size()));
1441 PP_Var str = var_interface->VarFromUtf8(text.c_str(),
1442 static_cast<uint32_t>(text.size()));
1443 const PPB_Console* console_interface =
1444 static_cast<const PPB_Console*>(
1445 module->GetBrowserInterface(PPB_CONSOLE_INTERFACE));
1446 console_interface->LogWithSource(pp_instance(), PP_LOGLEVEL_LOG, prefix, str);
1447 var_interface->Release(prefix);
1448 var_interface->Release(str);
1451 void Plugin::set_last_error_string(const nacl::string& error) {
1452 DCHECK(nacl_interface_);
1453 nacl_interface_->SetReadOnlyProperty(pp_instance(),
1454 pp::Var("lastError").pp_var(),
1455 pp::Var(error).pp_var());
1458 void Plugin::set_nacl_ready_state(ReadyState state) {
1459 nacl_ready_state_ = state;
1460 DCHECK(nacl_interface_);
1461 nacl_interface_->SetReadOnlyProperty(pp_instance(),
1462 pp::Var("readyState").pp_var(),
1463 pp::Var(state).pp_var());
1466 void Plugin::set_exit_status(int exit_status) {
1467 pp::Core* core = pp::Module::Get()->core();
1468 if (core->IsMainThread()) {
1469 SetExitStatusOnMainThread(PP_OK, exit_status);
1471 pp::CompletionCallback callback =
1472 callback_factory_.NewCallback(&Plugin::SetExitStatusOnMainThread,
1474 core->CallOnMainThread(0, callback, 0);
1478 void Plugin::SetExitStatusOnMainThread(int32_t pp_error,
1480 DCHECK(pp::Module::Get()->core()->IsMainThread());
1481 DCHECK(nacl_interface_);
1482 exit_status_ = exit_status;
1483 nacl_interface_->SetReadOnlyProperty(pp_instance(),
1484 pp::Var("exitStatus").pp_var(),
1485 pp::Var(exit_status_).pp_var());
1489 } // namespace plugin