chromium/native_client_sdk/src/examples/demo/nacl_io_demo/example.js

// Copyright 2012 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

function moduleDidLoad() {
  common.hideModule();
}

function $(id) {
  return document.getElementById(id);
}

// Called by the common.js module.
function domContentLoaded(name, tc, config, width, height) {
  navigator.webkitPersistentStorage.requestQuota(5 * 1024 * 1024,
      function(bytes) {
        common.updateStatus(
            'Allocated ' + bytes + ' bytes of persistent storage.');
        common.attachDefaultListeners();
        common.createNaClModule(name, tc, config, width, height);
      },
      function(e) { alert('Failed to allocate space') });
}

// Called by the common.js module.
function attachListeners() {
  var radioEls = document.querySelectorAll('input[type="radio"]');
  for (var i = 0; i < radioEls.length; ++i) {
    radioEls[i].addEventListener('click', onRadioClicked);
  }

  // Wire up the 'click' event for each function's button.
  var functionEls = document.querySelectorAll('.function');
  for (var i = 0; i < functionEls.length; ++i) {
    var functionEl = functionEls[i];
    var id = functionEl.getAttribute('id');
    var buttonEl = functionEl.querySelector('button');

    // The function name matches the element id.
    var func = window[id];
    buttonEl.addEventListener('click', func);
  }

  $('pipe_input_box').addEventListener('keypress', onPipeInput)
  $('pipe_output').disabled = true;

  $('pipe_name').addEventListener('change',
                                  function() { $('pipe_output').value = ''; })
}

// Called with keypress events on the pipe input box
function onPipeInput(e) {
  // Create an arraybuffer containing the 16-bit char code
  // from the keypress event.
  var buffer = new ArrayBuffer(1*2);
  var bufferView = new Uint16Array(buffer);
  bufferView[0] = e.charCode;

  // Pass the buffer in a dictionary over the NaCl module
  var pipeSelect = $('pipe_name');
  var pipeName = pipeSelect[pipeSelect.selectedIndex].value;
  var message = {
    pipe: pipeName,
    operation: 'write',
    payload: buffer,
  };
  nacl_module.postMessage(message);
  e.preventDefault();
  return false;
}

function onRadioClicked(e) {
  var divId = this.id.slice(5);  // skip "radio"
  var functionEls = document.querySelectorAll('.function');
  for (var i = 0; i < functionEls.length; ++i) {
    var visible = functionEls[i].id === divId;
    if (functionEls[i].id === divId)
      functionEls[i].removeAttribute('hidden');
    else
      functionEls[i].setAttribute('hidden', '');
  }
}

function addNameToSelectElements(cssClass, handle, name) {
  var text = '[' + handle + '] ' + name;
  var selectEls = document.querySelectorAll(cssClass);
  for (var i = 0; i < selectEls.length; ++i) {
    var optionEl = document.createElement('option');
    optionEl.setAttribute('value', handle);
    optionEl.appendChild(document.createTextNode(text));
    selectEls[i].appendChild(optionEl);
  }
}

function removeNameFromSelectElements(cssClass, handle) {
  var optionEls = document.querySelectorAll(cssClass + ' > option');
  for (var i = 0; i < optionEls.length; ++i) {
    var optionEl = optionEls[i];
    if (optionEl.value == handle) {
      var selectEl = optionEl.parentNode;
      selectEl.removeChild(optionEl);
    }
  }
}

var filehandle_map = {};
var dirhandle_map = {};

function fopen(e) {
  var filename = $('fopenFilename').value;
  var access = $('fopenMode').value;
  postCall('fopen', filename, access, function(filename, filehandle) {
    filehandle_map[filehandle] = filename;

    addNameToSelectElements('.file-handle', filehandle, filename);
    common.logMessage('File ' + filename + ' opened successfully.');
  });
}

function fclose(e) {
  var filehandle = parseInt($('fcloseHandle').value, 10);
  postCall('fclose', filehandle, function(filehandle) {
    var filename = filehandle_map[filehandle];
    removeNameFromSelectElements('.file-handle', filehandle, filename);
    common.logMessage('File ' + filename + ' closed successfully.');
  });
}

function fread(e) {
  var filehandle = parseInt($('freadHandle').value, 10);
  var numBytes = parseInt($('freadBytes').value, 10);
  postCall('fread', filehandle, numBytes, function(filehandle, data) {
    var filename = filehandle_map[filehandle];
    common.logMessage('Read "' + data + '" from file ' + filename + '.');
  });
}

function fwrite(e) {
  var filehandle = parseInt($('fwriteHandle').value, 10);
  var data = $('fwriteData').value;
  postCall('fwrite', filehandle, data, function(filehandle, bytesWritten) {
    var filename = filehandle_map[filehandle];
    common.logMessage('Wrote ' + bytesWritten + ' bytes to file ' + filename +
        '.');
  });
}

function fseek(e) {
  var filehandle = parseInt($('fseekHandle').value, 10);
  var offset = parseInt($('fseekOffset').value, 10);
  var whence = parseInt($('fseekWhence').value, 10);
  postCall('fseek', filehandle, offset, whence, function(filehandle, filepos) {
    var filename = filehandle_map[filehandle];
    common.logMessage('Seeked to location ' + filepos + ' in file ' + filename +
        '.');
  });
}

function fflush(e) {
  var filehandle = parseInt($('fflushHandle').value, 10);
  postCall('fflush', filehandle, function(filehandle, filepos) {
    var filename = filehandle_map[filehandle];
    common.logMessage('flushed ' + filename + '.');
  });
}

function stat(e) {
  var filename = $('statFilename').value;
  postCall('stat', filename, function(filename, size) {
    common.logMessage('File ' + filename + ' has size ' + size + '.');
  });
}

function opendir(e) {
  var dirname = $('opendirDirname').value;
  postCall('opendir', dirname, function(dirname, dirhandle) {
    dirhandle_map[dirhandle] = dirname;

    addNameToSelectElements('.dir-handle', dirhandle, dirname);
    common.logMessage('Directory ' + dirname + ' opened successfully.');
  });
}

function readdir(e) {
  var dirhandle = parseInt($('readdirHandle').value, 10);
  postCall('readdir', dirhandle, function(dirhandle, ino, name) {
    var dirname = dirhandle_map[dirhandle];
    if (ino === undefined) {
      common.logMessage('End of directory.');
    } else {
      common.logMessage('Read entry ("' + name + '", ino = ' + ino +
                        ') from directory ' + dirname + '.');
    }
  });
}

function closedir(e) {
  var dirhandle = parseInt($('closedirHandle').value, 10);
  postCall('closedir', dirhandle, function(dirhandle) {
    var dirname = dirhandle_map[dirhandle];
    delete dirhandle_map[dirhandle];

    removeNameFromSelectElements('.dir-handle', dirhandle, dirname);
    common.logMessage('Directory ' + dirname + ' closed successfully.');
  });
}

function mkdir(e) {
  var dirname = $('mkdirDirname').value;
  var mode = parseInt($('mkdirMode').value, 10);
  postCall('mkdir', dirname, mode, function(dirname) {
    common.logMessage('Directory ' + dirname + ' created successfully.');
  });
}

function rmdir(e) {
  var dirname = $('rmdirDirname').value;
  postCall('rmdir', dirname, function(dirname) {
    common.logMessage('Directory ' + dirname + ' removed successfully.');
  });
}

function chdir(e) {
  var dirname = $('chdirDirname').value;
  postCall('chdir', dirname, function(dirname) {
    common.logMessage('Changed directory to: ' + dirname + '.');
  });
}

function getcwd(e) {
  postCall('getcwd', function(dirname) {
    common.logMessage('getcwd: ' + dirname + '.');
  });
}

function getaddrinfo(e) {
  var name = $('getaddrinfoName').value;
  var family = $('getaddrinfoFamily').value;
  postCall('getaddrinfo', name, family, function(name, addrType) {
    common.logMessage('getaddrinfo returned successfully');
    common.logMessage('ai_cannonname = ' + name + '.');
    var count = 1;
    for (var i = 1; i < arguments.length; i+=2) {
      var msg = 'Address number ' + count + ' = ' + arguments[i] +
                ' (' + arguments[i+1] + ')';
      common.logMessage(msg);
      count += 1;
    }
  });
}

function gethostbyname(e) {
  var name = $('gethostbynameName').value;
  postCall('gethostbyname', name, function(name, addrType) {
    common.logMessage('gethostbyname returned successfully');
    common.logMessage('h_name = ' + name + '.');
    common.logMessage('h_addr_type = ' + addrType + '.');
    for (var i = 2; i < arguments.length; i++) {
      common.logMessage('Address number ' + (i-1) + ' = ' + arguments[i] + '.');
    }
  });
}

function connect(e) {
  var host = $('connectHost').value;
  var port = parseInt($('connectPort').value, 10);
  postCall('connect', host, port, function(sockhandle) {
    common.logMessage('connected');
    addNameToSelectElements('.sock-handle', sockhandle, '[socket]');
  });
}

function recv(e) {
  var handle = parseInt($('recvHandle').value, 10);
  var bufferSize = parseInt($('recvBufferSize').value, 10);
  postCall('recv', handle, bufferSize, function(messageLen, message) {
    common.logMessage("received " + messageLen + ' bytes: ' + message);
  });
}

function send(e) {
  var handle = parseInt($('sendHandle').value, 10);
  var message = $('sendMessage').value;
  postCall('send', handle, message, function(sentBytes) {
    common.logMessage("sent bytes: " + sentBytes);
  });
}

function close(e) {
  var handle = parseInt($('closeHandle').value, 10);
  postCall('close', handle, function(sock) {
    removeNameFromSelectElements('.sock-handle', sock, "[socket]");
    common.logMessage("closed socket: " + sock);
  });
}

var funcToCallback = {};

function postCall(func) {
  var callback = arguments[arguments.length - 1];
  funcToCallback[func] = callback;

  nacl_module.postMessage({
    cmd: func,
    args: Array.prototype.slice.call(arguments, 1, -1)
  });
}

function ArrayBufferToString(buf) {
  return String.fromCharCode.apply(null, new Uint16Array(buf));
}

// Called by the common.js module.
function handleMessage(message_event) {
  var data = message_event.data;
  if ((typeof(data) === 'string' || data instanceof String)) {
    common.logMessage(data);
  } else if (data instanceof Object) {
    var pipeName = data['pipe']
    if (pipeName !== undefined) {
      // Message for JavaScript I/O pipe
      var operation = data['operation'];
      if (operation == 'write') {
        $('pipe_output').value += ArrayBufferToString(data['payload']);
      } else if (operation == 'ack') {
        common.logMessage(pipeName + ": ack:" + data['payload']);
      } else {
        common.logMessage('Got unexpected pipe operation: ' + operation);
      }
    } else {
      // Result from a function call.
      var params = data.args;
      var funcName = data.cmd;
      var callback = funcToCallback[funcName];

      if (!callback) {
        common.logMessage('Error: Bad message ' + funcName +
                          ' received from NaCl module.');
        return;
      }

      delete funcToCallback[funcName];
      callback.apply(null, params);
    }
  } else {
    common.logMessage('Error: Unknow message `' + data +
                      '` received from NaCl module.');
  }
}