blob: 23a5089075a3d950e5b5761dc155a10e4e16799c [file] [log] [blame]
/*
* Copyright (c) 2013 The Chromium Authors. All rights reserved.
* Use of this source code is governed by a BSD-style license that can be
* found in the LICENSE file.
*/
'use strict';
lib.rtdep('lib.f',
'hterm');
// CSP means that we can't kick off the initialization from the html file,
// so we do it like this instead.
window.onload = function() {
lib.init(function() {
NaClTerm.init();
});
};
/**
* The hterm-powered terminal command.
*
* This class defines a command that can be run in an hterm.Terminal instance.
*
* @param {Object} argv The argument object passed in from the Terminal.
*/
function NaClTerm(argv) {
this.argv_ = argv;
this.io = null;
};
var embed;
var ansiCyan = '\x1b[36m';
var ansiReset = '\x1b[0m';
/**
* Static initialier called from index.html.
*
* This constructs a new Terminal instance and instructs it to run the NaClTerm
* command.
*/
NaClTerm.init = function() {
var profileName = lib.f.parseQuery(document.location.search)['profile'];
var terminal = new hterm.Terminal(profileName);
terminal.decorate(document.querySelector('#terminal'));
// Useful for console debugging.
window.term_ = terminal;
// We don't properly support the hterm bell sound, so we need to disable it.
terminal.prefs_.definePreference('audible-bell-sound', '');
terminal.setAutoCarriageReturn(true);
terminal.setCursorPosition(0, 0);
terminal.setCursorVisible(true);
terminal.runCommandClass(NaClTerm, document.location.hash.substr(1));
return true;
};
/**
* Handle messages sent to us from NaCl.
*
* @private
*/
NaClTerm.prototype.handleMessage_ = function(e) {
if (e.data.indexOf(NaClTerm.prefix) != 0) return;
var msg = e.data.substring(NaClTerm.prefix.length);
if (!this.loaded) {
this.bufferedOutput += msg;
} else {
term_.io.print(msg);
}
}
/**
* Handle load error event from NaCl.
*/
NaClTerm.prototype.handleLoadAbort_ = function(e) {
term_.io.print('Load aborted.\n');
}
/**
* Handle load abort event from NaCl.
*/
NaClTerm.prototype.handleLoadError_ = function(e) {
term_.io.print(embed.lastError + '\n');
}
NaClTerm.prototype.doneLoadingUrl = function() {
var width = term_.io.terminal_.screenSize.width;
term_.io.print('\r' + Array(width+1).join(' '));
var message = '\rLoaded ' + this.lastUrl;
if (this.lastTotal) {
var kbsize = Math.round(this.lastTotal/1024)
message += ' ['+ kbsize + ' KiB]';
}
term_.io.print(message.slice(0, width) + '\n')
}
/**
* Handle load end event from NaCl.
*/
NaClTerm.prototype.handleLoad_ = function(e) {
if (this.lastUrl)
this.doneLoadingUrl();
else
term_.io.print('Loaded.\n');
term_.io.print(ansiReset);
// Now that have completed loading and displaying
// loading messages we output any messages from the
// NaCl module that were buffered up unto this point
this.loaded = true;
term_.io.print(this.bufferedOutput);
this.bufferedOutput = ''
}
/**
* Handle load progress event from NaCl.
*/
NaClTerm.prototype.handleProgress_ = function(e) {
var url = e.url.substring(e.url.lastIndexOf('/') + 1);
if (this.lastUrl && this.lastUrl != url)
this.doneLoadingUrl()
if (!url)
return;
var message = 'Loading ' + url;
if (e.lengthComputable && e.total) {
var percent = Math.round(e.loaded * 100 / e.total);
var kbloaded = Math.round(e.loaded / 1024);
var kbtotal = Math.round(e.total / 1024);
message += ' [' + kbloaded + ' KiB/' + kbtotal + ' KiB ' + percent + '%]';
}
var width = term_.io.terminal_.screenSize.width;
term_.io.print('\r' + message.slice(-width));
this.lastUrl = url;
this.lastTotal = e.total;
}
/**
* Handle crash event from NaCl.
*/
NaClTerm.prototype.handleCrash_ = function(e) {
term_.io.print(ansiCyan)
if (embed.exitStatus == -1) {
term_.io.print('Program crashed (exit status -1)\n')
} else {
term_.io.print('Program exited (status=' + embed.exitStatus + ')\n');
}
}
NaClTerm.prototype.onTerminalResize_ = function() {
var width = term_.io.terminal_.screenSize.width;
var height = term_.io.terminal_.screenSize.height;
embed.postMessage({'tty_resize': [ width, height ]});
}
NaClTerm.prototype.onVTKeystroke_ = function(str) {
var message = {};
message[NaClTerm.prefix] = str;
embed.postMessage(message);
}
/*
* This is invoked by the terminal as a result of terminal.runCommandClass().
*/
NaClTerm.prototype.run = function() {
this.io = this.argv_.io.push();
this.bufferedOutput = '';
this.loaded = false;
this.io.print(ansiCyan);
var mimetype = 'application/x-pnacl';
if (navigator.mimeTypes[mimetype] === undefined) {
if (mimetype.indexOf('pnacl') != -1)
this.io.print('Browser does not support PNaCl or PNaCl is disabled\n');
else
this.io.print('Browser does not support NaCl or NaCl is disabled\n');
return;
}
embed = document.createElement('object');
embed.width = 0;
embed.height = 0;
embed.data = NaClTerm.nmf;
embed.type = mimetype;
embed.addEventListener('message', this.handleMessage_.bind(this));
embed.addEventListener('progress', this.handleProgress_.bind(this));
embed.addEventListener('load', this.handleLoad_.bind(this));
embed.addEventListener('error', this.handleLoadError_.bind(this));
embed.addEventListener('abort', this.handleLoadAbort_.bind(this));
embed.addEventListener('crash', this.handleCrash_.bind(this));
function addParam(name, value) {
var param = document.createElement('param');
param.name = name;
param.value = value;
embed.appendChild(param);
}
addParam('ps_tty_prefix', NaClTerm.prefix);
addParam('ps_tty_resize', 'tty_resize');
addParam('ps_stdin', '/dev/tty');
addParam('ps_stdout', '/dev/tty');
addParam('ps_stderr', '/dev/tty');
addParam('ps_verbosity', '2');
addParam('TERM', 'xterm-256color');
var args = lib.f.parseQuery(document.location.search);
var argn = 1;
while (true) {
var argname = 'arg' + argn;
var arg = args[argname];
if (typeof(arg) === 'undefined')
break;
addParam(argname, arg);
argn = argn + 1;
}
this.io.print('Loading NaCl module.\n')
document.body.appendChild(embed);
this.io.onVTKeystroke = this.onVTKeystroke_.bind(this);
this.io.onTerminalResize = this.onTerminalResize_.bind(this);
};