434 lines
16 KiB
C++
434 lines
16 KiB
C++
|
|
// Copyright 2014 The Chromium Authors
|
||
|
|
// Use of this source code is governed by a BSD-style license that can be
|
||
|
|
// found in the LICENSE file.
|
||
|
|
|
||
|
|
#include "components/nacl/renderer/nexe_load_manager.h"
|
||
|
|
|
||
|
|
#include <stddef.h>
|
||
|
|
|
||
|
|
#include <utility>
|
||
|
|
|
||
|
|
#include "base/command_line.h"
|
||
|
|
#include "base/containers/buffer_iterator.h"
|
||
|
|
#include "base/logging.h"
|
||
|
|
#include "base/memory/shared_memory_mapping.h"
|
||
|
|
#include "base/metrics/histogram.h"
|
||
|
|
#include "base/strings/string_tokenizer.h"
|
||
|
|
#include "base/strings/string_util.h"
|
||
|
|
#include "components/nacl/common/nacl_host_messages.h"
|
||
|
|
#include "components/nacl/common/nacl_types.h"
|
||
|
|
#include "components/nacl/renderer/histogram.h"
|
||
|
|
#include "components/nacl/renderer/manifest_service_channel.h"
|
||
|
|
#include "components/nacl/renderer/platform_info.h"
|
||
|
|
#include "components/nacl/renderer/pnacl_translation_resource_host.h"
|
||
|
|
#include "components/nacl/renderer/progress_event.h"
|
||
|
|
#include "components/nacl/renderer/trusted_plugin_channel.h"
|
||
|
|
#include "content/public/common/content_switches.h"
|
||
|
|
#include "content/public/renderer/pepper_plugin_instance.h"
|
||
|
|
#include "content/public/renderer/render_thread.h"
|
||
|
|
#include "content/public/renderer/renderer_ppapi_host.h"
|
||
|
|
#include "ppapi/c/pp_bool.h"
|
||
|
|
#include "ppapi/c/private/pp_file_handle.h"
|
||
|
|
#include "ppapi/shared_impl/ppapi_globals.h"
|
||
|
|
#include "ppapi/shared_impl/ppapi_permissions.h"
|
||
|
|
#include "ppapi/shared_impl/ppapi_preferences.h"
|
||
|
|
#include "ppapi/shared_impl/scoped_pp_var.h"
|
||
|
|
#include "ppapi/shared_impl/var.h"
|
||
|
|
#include "ppapi/shared_impl/var_tracker.h"
|
||
|
|
#include "ppapi/thunk/enter.h"
|
||
|
|
#include "third_party/blink/public/platform/web_url.h"
|
||
|
|
#include "third_party/blink/public/web/web_document.h"
|
||
|
|
#include "third_party/blink/public/web/web_plugin_container.h"
|
||
|
|
#include "third_party/blink/public/web/web_view.h"
|
||
|
|
#include "v8/include/v8.h"
|
||
|
|
|
||
|
|
namespace nacl {
|
||
|
|
|
||
|
|
namespace {
|
||
|
|
|
||
|
|
const char* const kTypeAttribute = "type";
|
||
|
|
// The "src" attribute of the <embed> tag. The value is expected to be either
|
||
|
|
// a URL or URI pointing to the manifest file (which is expected to contain
|
||
|
|
// JSON matching ISAs with .nexe URLs).
|
||
|
|
const char* const kSrcManifestAttribute = "src";
|
||
|
|
// The "nacl" attribute of the <embed> tag. We use the value of this attribute
|
||
|
|
// to find the manifest file when NaCl is registered as a plugin for another
|
||
|
|
// MIME type because the "src" attribute is used to supply us with the resource
|
||
|
|
// of that MIME type that we're supposed to display.
|
||
|
|
const char* const kNaClManifestAttribute = "nacl";
|
||
|
|
|
||
|
|
const char* const kNaClMIMEType = "application/x-nacl";
|
||
|
|
const char* const kPNaClMIMEType = "application/x-pnacl";
|
||
|
|
|
||
|
|
static int GetRoutingID(PP_Instance instance) {
|
||
|
|
// Check that we are on the main renderer thread.
|
||
|
|
DCHECK(content::RenderThread::Get());
|
||
|
|
content::RendererPpapiHost *host =
|
||
|
|
content::RendererPpapiHost::GetForPPInstance(instance);
|
||
|
|
if (!host)
|
||
|
|
return 0;
|
||
|
|
return host->GetRoutingIDForFrame(instance);
|
||
|
|
}
|
||
|
|
|
||
|
|
std::string LookupAttribute(const std::map<std::string, std::string>& args,
|
||
|
|
const std::string& key) {
|
||
|
|
auto it = args.find(key);
|
||
|
|
if (it != args.end())
|
||
|
|
return it->second;
|
||
|
|
return std::string();
|
||
|
|
}
|
||
|
|
|
||
|
|
} // namespace
|
||
|
|
|
||
|
|
NexeLoadManager::NexeLoadManager(PP_Instance pp_instance)
|
||
|
|
: pp_instance_(pp_instance),
|
||
|
|
nacl_ready_state_(PP_NACL_READY_STATE_UNSENT),
|
||
|
|
nexe_error_reported_(false),
|
||
|
|
is_installed_(false),
|
||
|
|
exit_status_(-1),
|
||
|
|
nexe_size_(0),
|
||
|
|
plugin_instance_(content::PepperPluginInstance::Get(pp_instance)) {
|
||
|
|
set_exit_status(-1);
|
||
|
|
SetLastError("");
|
||
|
|
HistogramEnumerateOsArch(GetSandboxArch());
|
||
|
|
if (plugin_instance_) {
|
||
|
|
plugin_base_url_ = plugin_instance_->GetContainer()->GetDocument().Url();
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
NexeLoadManager::~NexeLoadManager() {
|
||
|
|
if (!nexe_error_reported_) {
|
||
|
|
base::TimeDelta uptime = base::Time::Now() - ready_time_;
|
||
|
|
HistogramTimeLarge("NaCl.ModuleUptime.Normal", uptime.InMilliseconds());
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
void NexeLoadManager::NexeFileDidOpen(int32_t pp_error,
|
||
|
|
const base::File& file,
|
||
|
|
int32_t http_status,
|
||
|
|
int64_t nexe_bytes_read,
|
||
|
|
const std::string& url,
|
||
|
|
base::TimeDelta time_since_open) {
|
||
|
|
// Check that we are on the main renderer thread.
|
||
|
|
DCHECK(content::RenderThread::Get());
|
||
|
|
VLOG(1) << "Plugin::NexeFileDidOpen (pp_error=" << pp_error << ")";
|
||
|
|
HistogramHTTPStatusCode(
|
||
|
|
is_installed_ ? "NaCl.HttpStatusCodeClass.Nexe.InstalledApp" :
|
||
|
|
"NaCl.HttpStatusCodeClass.Nexe.NotInstalledApp",
|
||
|
|
http_status);
|
||
|
|
|
||
|
|
if (pp_error != PP_OK || !file.IsValid()) {
|
||
|
|
if (pp_error == PP_ERROR_ABORTED) {
|
||
|
|
ReportLoadAbort();
|
||
|
|
} else if (pp_error == PP_ERROR_NOACCESS) {
|
||
|
|
ReportLoadError(PP_NACL_ERROR_NEXE_NOACCESS_URL,
|
||
|
|
"access to nexe url was denied.");
|
||
|
|
} else {
|
||
|
|
ReportLoadError(PP_NACL_ERROR_NEXE_LOAD_URL,
|
||
|
|
"could not load nexe url.");
|
||
|
|
}
|
||
|
|
} else if (nexe_bytes_read == -1) {
|
||
|
|
ReportLoadError(PP_NACL_ERROR_NEXE_STAT, "could not stat nexe file.");
|
||
|
|
} else {
|
||
|
|
// TODO(dmichael): Can we avoid stashing away so much state?
|
||
|
|
nexe_size_ = nexe_bytes_read;
|
||
|
|
HistogramSizeKB("NaCl.Perf.Size.Nexe",
|
||
|
|
static_cast<int32_t>(nexe_size_ / 1024));
|
||
|
|
HistogramStartupTimeMedium(
|
||
|
|
"NaCl.Perf.StartupTime.NexeDownload", time_since_open, nexe_size_);
|
||
|
|
|
||
|
|
// Inform JavaScript that we successfully downloaded the nacl module.
|
||
|
|
ProgressEvent progress_event(PP_NACL_EVENT_PROGRESS, url, true, nexe_size_,
|
||
|
|
nexe_size_);
|
||
|
|
DispatchProgressEvent(pp_instance_, progress_event);
|
||
|
|
load_start_ = base::Time::Now();
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
void NexeLoadManager::ReportLoadSuccess(const std::string& url,
|
||
|
|
uint64_t loaded_bytes,
|
||
|
|
uint64_t total_bytes) {
|
||
|
|
ready_time_ = base::Time::Now();
|
||
|
|
if (!IsPNaCl()) {
|
||
|
|
base::TimeDelta load_module_time = ready_time_ - load_start_;
|
||
|
|
HistogramStartupTimeSmall(
|
||
|
|
"NaCl.Perf.StartupTime.LoadModule", load_module_time, nexe_size_);
|
||
|
|
HistogramStartupTimeMedium(
|
||
|
|
"NaCl.Perf.StartupTime.Total", ready_time_ - init_time_, nexe_size_);
|
||
|
|
}
|
||
|
|
|
||
|
|
// Check that we are on the main renderer thread.
|
||
|
|
DCHECK(content::RenderThread::Get());
|
||
|
|
set_nacl_ready_state(PP_NACL_READY_STATE_DONE);
|
||
|
|
|
||
|
|
// Inform JavaScript that loading was successful and is complete.
|
||
|
|
ProgressEvent load_event(PP_NACL_EVENT_LOAD, url, true, loaded_bytes,
|
||
|
|
total_bytes);
|
||
|
|
DispatchProgressEvent(pp_instance_, load_event);
|
||
|
|
|
||
|
|
ProgressEvent loadend_event(PP_NACL_EVENT_LOADEND, url, true, loaded_bytes,
|
||
|
|
total_bytes);
|
||
|
|
DispatchProgressEvent(pp_instance_, loadend_event);
|
||
|
|
|
||
|
|
// UMA
|
||
|
|
HistogramEnumerateLoadStatus(PP_NACL_ERROR_LOAD_SUCCESS, is_installed_);
|
||
|
|
}
|
||
|
|
|
||
|
|
void NexeLoadManager::ReportLoadError(PP_NaClError error,
|
||
|
|
const std::string& error_message) {
|
||
|
|
ReportLoadError(error, error_message, error_message);
|
||
|
|
}
|
||
|
|
|
||
|
|
void NexeLoadManager::ReportLoadError(PP_NaClError error,
|
||
|
|
const std::string& error_message,
|
||
|
|
const std::string& console_message) {
|
||
|
|
// Check that we are on the main renderer thread.
|
||
|
|
DCHECK(content::RenderThread::Get());
|
||
|
|
|
||
|
|
if (error == PP_NACL_ERROR_MANIFEST_PROGRAM_MISSING_ARCH) {
|
||
|
|
// A special case: the manifest may otherwise be valid but is missing
|
||
|
|
// a program/file compatible with the user's sandbox.
|
||
|
|
IPC::Sender* sender = content::RenderThread::Get();
|
||
|
|
sender->Send(
|
||
|
|
new NaClHostMsg_MissingArchError(GetRoutingID(pp_instance_)));
|
||
|
|
}
|
||
|
|
set_nacl_ready_state(PP_NACL_READY_STATE_DONE);
|
||
|
|
nexe_error_reported_ = true;
|
||
|
|
|
||
|
|
// We must set all properties before calling DispatchEvent so that when an
|
||
|
|
// event handler runs, the properties reflect the current load state.
|
||
|
|
std::string error_string = std::string("NaCl module load failed: ") +
|
||
|
|
std::string(error_message);
|
||
|
|
SetLastError(error_string);
|
||
|
|
|
||
|
|
// Inform JavaScript that loading encountered an error and is complete.
|
||
|
|
DispatchProgressEvent(pp_instance_, ProgressEvent(PP_NACL_EVENT_ERROR));
|
||
|
|
DispatchProgressEvent(pp_instance_, ProgressEvent(PP_NACL_EVENT_LOADEND));
|
||
|
|
|
||
|
|
HistogramEnumerateLoadStatus(error, is_installed_);
|
||
|
|
LogToConsole(console_message);
|
||
|
|
}
|
||
|
|
|
||
|
|
void NexeLoadManager::ReportLoadAbort() {
|
||
|
|
// Check that we are on the main renderer thread.
|
||
|
|
DCHECK(content::RenderThread::Get());
|
||
|
|
|
||
|
|
// Set the readyState attribute to indicate we need to start over.
|
||
|
|
set_nacl_ready_state(PP_NACL_READY_STATE_DONE);
|
||
|
|
nexe_error_reported_ = true;
|
||
|
|
|
||
|
|
// Report an error in lastError and on the JavaScript console.
|
||
|
|
std::string error_string("NaCl module load failed: user aborted");
|
||
|
|
SetLastError(error_string);
|
||
|
|
|
||
|
|
// Inform JavaScript that loading was aborted and is complete.
|
||
|
|
DispatchProgressEvent(pp_instance_, ProgressEvent(PP_NACL_EVENT_ABORT));
|
||
|
|
DispatchProgressEvent(pp_instance_, ProgressEvent(PP_NACL_EVENT_LOADEND));
|
||
|
|
|
||
|
|
HistogramEnumerateLoadStatus(PP_NACL_ERROR_LOAD_ABORTED, is_installed_);
|
||
|
|
LogToConsole(error_string);
|
||
|
|
}
|
||
|
|
|
||
|
|
void NexeLoadManager::NexeDidCrash() {
|
||
|
|
VLOG(1) << "Plugin::NexeDidCrash: crash event!";
|
||
|
|
// The NaCl module voluntarily exited. However, this is still a
|
||
|
|
// crash from the point of view of Pepper, since PPAPI plugins are
|
||
|
|
// event handlers and should never exit.
|
||
|
|
VLOG_IF(1, exit_status_ != -1)
|
||
|
|
<< "Plugin::NexeDidCrash: nexe exited with status " << exit_status_
|
||
|
|
<< " so this is a \"controlled crash\".";
|
||
|
|
// If the crash occurs during load, we just want to report an error
|
||
|
|
// that fits into our load progress event grammar. If the crash
|
||
|
|
// occurs after loaded/loadend, then we use ReportDeadNexe to send a
|
||
|
|
// "crash" event.
|
||
|
|
if (nexe_error_reported_) {
|
||
|
|
VLOG(1) << "Plugin::NexeDidCrash: error already reported; suppressing";
|
||
|
|
} else {
|
||
|
|
if (nacl_ready_state_ == PP_NACL_READY_STATE_DONE) {
|
||
|
|
ReportDeadNexe();
|
||
|
|
} else {
|
||
|
|
ReportLoadError(PP_NACL_ERROR_START_PROXY_CRASH,
|
||
|
|
"Nexe crashed during startup");
|
||
|
|
}
|
||
|
|
}
|
||
|
|
// In all cases, try to grab the crash log. The first error
|
||
|
|
// reported may have come from the start_module RPC reply indicating
|
||
|
|
// a validation error or something similar, which wouldn't grab the
|
||
|
|
// crash log. In the event that this is called twice, the second
|
||
|
|
// invocation will just be a no-op, since the entire crash log will
|
||
|
|
// have been received and we'll just get an EOF indication.
|
||
|
|
|
||
|
|
base::ReadOnlySharedMemoryMapping shmem_mapping =
|
||
|
|
crash_info_shmem_region_.MapAt(0, kNaClCrashInfoShmemSize);
|
||
|
|
if (shmem_mapping.IsValid()) {
|
||
|
|
auto buffer = base::BufferIterator<const uint8_t>(
|
||
|
|
shmem_mapping.GetMemoryAsSpan<uint8_t>());
|
||
|
|
const uint32_t* crash_log_length = buffer.Object<uint32_t>();
|
||
|
|
base::span<const uint8_t> data = buffer.Span<uint8_t>(
|
||
|
|
std::min<uint32_t>(*crash_log_length, kNaClCrashInfoMaxLogSize));
|
||
|
|
std::string crash_log(data.begin(), data.end());
|
||
|
|
CopyCrashLogToJsConsole(crash_log);
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
void NexeLoadManager::set_trusted_plugin_channel(
|
||
|
|
std::unique_ptr<TrustedPluginChannel> channel) {
|
||
|
|
trusted_plugin_channel_ = std::move(channel);
|
||
|
|
}
|
||
|
|
|
||
|
|
void NexeLoadManager::set_manifest_service_channel(
|
||
|
|
std::unique_ptr<ManifestServiceChannel> channel) {
|
||
|
|
manifest_service_channel_ = std::move(channel);
|
||
|
|
}
|
||
|
|
|
||
|
|
PP_NaClReadyState NexeLoadManager::nacl_ready_state() {
|
||
|
|
return nacl_ready_state_;
|
||
|
|
}
|
||
|
|
|
||
|
|
void NexeLoadManager::set_nacl_ready_state(PP_NaClReadyState ready_state) {
|
||
|
|
nacl_ready_state_ = ready_state;
|
||
|
|
ppapi::ScopedPPVar ready_state_name(
|
||
|
|
ppapi::ScopedPPVar::PassRef(),
|
||
|
|
ppapi::StringVar::StringToPPVar("readyState"));
|
||
|
|
SetReadOnlyProperty(ready_state_name.get(), PP_MakeInt32(ready_state));
|
||
|
|
}
|
||
|
|
|
||
|
|
void NexeLoadManager::SetLastError(const std::string& error) {
|
||
|
|
ppapi::ScopedPPVar error_name_var(
|
||
|
|
ppapi::ScopedPPVar::PassRef(),
|
||
|
|
ppapi::StringVar::StringToPPVar("lastError"));
|
||
|
|
ppapi::ScopedPPVar error_var(
|
||
|
|
ppapi::ScopedPPVar::PassRef(),
|
||
|
|
ppapi::StringVar::StringToPPVar(error));
|
||
|
|
SetReadOnlyProperty(error_name_var.get(), error_var.get());
|
||
|
|
}
|
||
|
|
|
||
|
|
void NexeLoadManager::SetReadOnlyProperty(PP_Var key, PP_Var value) {
|
||
|
|
plugin_instance_->SetEmbedProperty(key, value);
|
||
|
|
}
|
||
|
|
|
||
|
|
void NexeLoadManager::LogToConsole(const std::string& message) {
|
||
|
|
ppapi::PpapiGlobals::Get()->LogWithSource(
|
||
|
|
pp_instance_, PP_LOGLEVEL_LOG, std::string("NativeClient"), message);
|
||
|
|
}
|
||
|
|
|
||
|
|
void NexeLoadManager::set_exit_status(int exit_status) {
|
||
|
|
exit_status_ = exit_status;
|
||
|
|
ppapi::ScopedPPVar exit_status_name_var(
|
||
|
|
ppapi::ScopedPPVar::PassRef(),
|
||
|
|
ppapi::StringVar::StringToPPVar("exitStatus"));
|
||
|
|
SetReadOnlyProperty(exit_status_name_var.get(), PP_MakeInt32(exit_status));
|
||
|
|
}
|
||
|
|
|
||
|
|
void NexeLoadManager::InitializePlugin(
|
||
|
|
uint32_t argc, const char* argn[], const char* argv[]) {
|
||
|
|
init_time_ = base::Time::Now();
|
||
|
|
|
||
|
|
for (size_t i = 0; i < argc; ++i) {
|
||
|
|
std::string name(argn[i]);
|
||
|
|
std::string value(argv[i]);
|
||
|
|
args_[name] = value;
|
||
|
|
}
|
||
|
|
|
||
|
|
// Store mime_type_ at initialization time since we make it lowercase.
|
||
|
|
mime_type_ = base::ToLowerASCII(LookupAttribute(args_, kTypeAttribute));
|
||
|
|
}
|
||
|
|
|
||
|
|
void NexeLoadManager::ReportStartupOverhead() const {
|
||
|
|
base::TimeDelta overhead = base::Time::Now() - init_time_;
|
||
|
|
HistogramStartupTimeMedium(
|
||
|
|
"NaCl.Perf.StartupTime.NaClOverhead", overhead, nexe_size_);
|
||
|
|
}
|
||
|
|
|
||
|
|
bool NexeLoadManager::RequestNaClManifest(const std::string& url) {
|
||
|
|
if (plugin_base_url_.is_valid()) {
|
||
|
|
const GURL& resolved_url = plugin_base_url_.Resolve(url);
|
||
|
|
if (resolved_url.is_valid()) {
|
||
|
|
manifest_base_url_ = resolved_url;
|
||
|
|
is_installed_ = manifest_base_url_.SchemeIs("chrome-extension");
|
||
|
|
HistogramEnumerateManifestIsDataURI(
|
||
|
|
manifest_base_url_.SchemeIs("data"));
|
||
|
|
set_nacl_ready_state(PP_NACL_READY_STATE_OPENED);
|
||
|
|
DispatchProgressEvent(pp_instance_,
|
||
|
|
ProgressEvent(PP_NACL_EVENT_LOADSTART));
|
||
|
|
return true;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
ReportLoadError(PP_NACL_ERROR_MANIFEST_RESOLVE_URL,
|
||
|
|
std::string("could not resolve URL \"") + url +
|
||
|
|
"\" relative to \"" +
|
||
|
|
plugin_base_url_.possibly_invalid_spec() + "\".");
|
||
|
|
return false;
|
||
|
|
}
|
||
|
|
|
||
|
|
void NexeLoadManager::ProcessNaClManifest(const std::string& program_url) {
|
||
|
|
program_url_ = program_url;
|
||
|
|
GURL gurl(program_url);
|
||
|
|
DCHECK(gurl.is_valid());
|
||
|
|
if (gurl.is_valid())
|
||
|
|
is_installed_ = gurl.SchemeIs("chrome-extension");
|
||
|
|
set_nacl_ready_state(PP_NACL_READY_STATE_LOADING);
|
||
|
|
DispatchProgressEvent(pp_instance_, ProgressEvent(PP_NACL_EVENT_PROGRESS));
|
||
|
|
}
|
||
|
|
|
||
|
|
std::string NexeLoadManager::GetManifestURLArgument() const {
|
||
|
|
std::string manifest_url;
|
||
|
|
|
||
|
|
// If the MIME type is foreign, then this NEXE is being used as a content
|
||
|
|
// type handler rather than directly by an HTML document.
|
||
|
|
bool nexe_is_content_handler =
|
||
|
|
!mime_type_.empty() &&
|
||
|
|
mime_type_ != kNaClMIMEType &&
|
||
|
|
mime_type_ != kPNaClMIMEType;
|
||
|
|
|
||
|
|
if (nexe_is_content_handler) {
|
||
|
|
// For content handlers 'src' will be the URL for the content
|
||
|
|
// and 'nacl' will be the URL for the manifest.
|
||
|
|
manifest_url = LookupAttribute(args_, kNaClManifestAttribute);
|
||
|
|
} else {
|
||
|
|
manifest_url = LookupAttribute(args_, kSrcManifestAttribute);
|
||
|
|
}
|
||
|
|
|
||
|
|
if (manifest_url.empty()) {
|
||
|
|
VLOG(1) << "WARNING: no 'src' property, so no manifest loaded.";
|
||
|
|
if (args_.find(kNaClManifestAttribute) != args_.end())
|
||
|
|
VLOG(1) << "WARNING: 'nacl' property is incorrect. Use 'src'.";
|
||
|
|
}
|
||
|
|
return manifest_url;
|
||
|
|
}
|
||
|
|
|
||
|
|
void NexeLoadManager::CloseTrustedPluginChannel() {
|
||
|
|
trusted_plugin_channel_.reset();
|
||
|
|
}
|
||
|
|
|
||
|
|
bool NexeLoadManager::IsPNaCl() const {
|
||
|
|
return mime_type_ == kPNaClMIMEType;
|
||
|
|
}
|
||
|
|
|
||
|
|
void NexeLoadManager::ReportDeadNexe() {
|
||
|
|
if (nacl_ready_state_ == PP_NACL_READY_STATE_DONE && // After loadEnd
|
||
|
|
!nexe_error_reported_) {
|
||
|
|
// Crashes will be more likely near startup, so use a medium histogram
|
||
|
|
// instead of a large one.
|
||
|
|
base::TimeDelta uptime = base::Time::Now() - ready_time_;
|
||
|
|
HistogramTimeMedium("NaCl.ModuleUptime.Crash", uptime.InMilliseconds());
|
||
|
|
|
||
|
|
std::string message("NaCl module crashed");
|
||
|
|
SetLastError(message);
|
||
|
|
LogToConsole(message);
|
||
|
|
|
||
|
|
DispatchProgressEvent(pp_instance_, ProgressEvent(PP_NACL_EVENT_CRASH));
|
||
|
|
nexe_error_reported_ = true;
|
||
|
|
}
|
||
|
|
// else ReportLoadError() and ReportLoadAbort() will be used by loading code
|
||
|
|
// to provide error handling.
|
||
|
|
}
|
||
|
|
|
||
|
|
void NexeLoadManager::CopyCrashLogToJsConsole(const std::string& crash_log) {
|
||
|
|
base::StringTokenizer t(crash_log, "\n");
|
||
|
|
while (t.GetNext())
|
||
|
|
LogToConsole(t.token());
|
||
|
|
}
|
||
|
|
|
||
|
|
} // namespace nacl
|