Fix crosh handling of non-ASCII keyboard input.

Crosh should ensure that strings sent to chrome.terminalPrivate are UTF-16
encoded. If the keyboard character encoding is utf-8, the input should be
decoded before forwarding it to chrome.terminalPrivate.sendString

Change-Id: I14a8131b18e82dac191efbe50c666f8f613476ca
Reviewed-on: https://chromium-review.googlesource.com/198044
Reviewed-by: Robert Ginda <rginda@chromium.org>
Tested-by: Robert Ginda <rginda@chromium.org>
diff --git a/nassh/js/crosh.js b/nassh/js/crosh.js
index 80fee48..22c5b8c 100644
--- a/nassh/js/crosh.js
+++ b/nassh/js/crosh.js
@@ -26,6 +26,7 @@
 function Crosh(argv) {
   this.argv_ = argv;
   this.io = null;
+  this.keyboard_ = null;
   this.pid_ = -1;
 };
 
@@ -48,13 +49,14 @@
   var profileName = lib.f.parseQuery(document.location.search)['profile'];
   var terminal = new hterm.Terminal(profileName);
 
-  if (chrome.runtime.id != Crosh.croshBuiltinId) {
+  terminal.decorate(document.querySelector('#terminal'));
+  terminal.onTerminalReady = function() {
     // We want to override the Ctrl-Shift-N keystroke so it opens nassh.html,
     // and its connection dialog, rather than reloading crosh.html.
     //
     // The builtin version of crosh does not come with nassh, so it won't work
     // from there.
-    terminal.onTerminalReady = function() {
+    if (chrome.runtime.id != Crosh.croshBuiltinId) {
       var openSecureShell = function() {
           window.open('/html/nassh.html', '',
                       'chrome=no,close=yes,resize=yes,scrollbars=yes,' +
@@ -76,21 +78,17 @@
 
         return hterm.Keyboard.KeyActions.DEFAULT;
       };
-    };
-  }
+    }
 
-  terminal.decorate(document.querySelector('#terminal'));
+    terminal.setCursorPosition(0, 0);
+    terminal.setCursorVisible(true);
+    terminal.runCommandClass(Crosh, document.location.hash.substr(1));
+
+    terminal.command.keyboard_ = terminal.keyboard;
+  };
 
   // Useful for console debugging.
   window.term_ = terminal;
-
-  // Looks like there is a race between this and terminal initialization, thus
-  // adding timeout.
-  setTimeout(function() {
-      terminal.setCursorPosition(0, 0);
-      terminal.setCursorVisible(true);
-      terminal.runCommandClass(Crosh, document.location.hash.substr(1));
-    }, 500);
   return true;
 };
 
@@ -136,8 +134,8 @@
     return;
   }
 
-  this.io.onVTKeystroke = this.sendString_.bind(this);
-  this.io.sendString = this.sendString_.bind(this);
+  this.io.onVTKeystroke = this.sendString_.bind(this, true /* fromKeyboard */);
+  this.io.sendString = this.sendString_.bind(this, false /* fromKeyboard */);
 
   var self = this;
   this.io.onTerminalResize = this.onTerminalResize_.bind(this);
@@ -174,14 +172,40 @@
 };
 
 /**
+ * Used by {@code this.sendString_} to determine if a string should be UTF-8
+ * decoded to UTF-16 before sending it to {@code chrome.terminalPrivate}.
+ * The string should be decoded if it came from keyboard with 'utf-8' character
+ * encoding. The reason is that the extension system expects strings it handles
+ * to be UTF-16 encoded.
+ *
+ * @private
+ *
+ * @param {boolean} fromKeyboard Whether the string came from keyboard.
+ * @param {string} string A string that may be UTF-8 encoded.
+ *
+ * @return {string} If decoding is needed, the decoded string, otherwise the
+ *     original string.
+ */
+Crosh.prototype.decodeUTF8IfNeeded_ = function(fromKeyboard, string) {
+  if (fromKeyboard &&
+      this.keyboard_ && this.keyboard_.characterEncoding == 'utf-8') {
+    return lib.decodeUTF8(string);
+  }
+  return string;
+};
+
+/**
  * Send a string to the crosh process.
  *
+ * @param {boolean} fromKeyborad Whether the string originates from keyboard.
  * @param {string} string The string to send.
  */
-Crosh.prototype.sendString_ = function(string) {
+Crosh.prototype.sendString_ = function(fromKeyboard, string) {
   if (this.pid_ == -1)
     return;
-  chrome.terminalPrivate.sendInput(this.pid_, string);
+  chrome.terminalPrivate.sendInput(
+      this.pid_,
+      this.decodeUTF8IfNeeded_(fromKeyboard, string));
 };
 
 /**