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.
4
5function moduleDidLoad() {
6  common.hideModule();
7}
8
9function $(id) {
10  return document.getElementById(id);
11}
12
13// Called by the common.js module.
14function domContentLoaded(name, tc, config, width, height) {
15  navigator.webkitPersistentStorage.requestQuota(1024 * 1024,
16      function(bytes) {
17        common.updateStatus(
18            'Allocated ' + bytes + ' bytes of persistant storage.');
19        common.attachDefaultListeners();
20        common.createNaClModule(name, tc, config, width, height);
21      },
22      function(e) { alert('Failed to allocate space') });
23}
24
25// Called by the common.js module.
26function attachListeners() {
27  var radioEls = document.querySelectorAll('input[type="radio"]');
28  for (var i = 0; i < radioEls.length; ++i) {
29    radioEls[i].addEventListener('click', onRadioClicked);
30  }
31
32  // Wire up the 'click' event for each function's button.
33  var functionEls = document.querySelectorAll('.function');
34  for (var i = 0; i < functionEls.length; ++i) {
35    var functionEl = functionEls[i];
36    var id = functionEl.getAttribute('id');
37    var buttonEl = functionEl.querySelector('button');
38
39    // The function name matches the element id.
40    var func = window[id];
41    buttonEl.addEventListener('click', func);
42  }
43
44  $('pipe_input_box').addEventListener('keypress', onPipeInput)
45  $('pipe_output').disabled = true;
46
47  $('pipe_name').addEventListener('change',
48                                  function() { $('pipe_output').value = ''; })
49}
50
51// Called with keypress events on the pipe input box
52function onPipeInput(e) {
53  // Create an arraybuffer containing the 16-bit char code
54  // from the keypress event.
55  var buffer = new ArrayBuffer(1*2);
56  var bufferView = new Uint16Array(buffer);
57  bufferView[0] = e.charCode;
58
59  // Pass the buffer in a dictionary over the NaCl module
60  var pipeSelect = $('pipe_name');
61  var pipeName = pipeSelect[pipeSelect.selectedIndex].value;
62  var message = {
63    pipe: pipeName,
64    operation: 'write',
65    payload: buffer,
66  };
67  nacl_module.postMessage(message);
68  e.preventDefault();
69  return false;
70}
71
72function onRadioClicked(e) {
73  var divId = this.id.slice(5);  // skip "radio"
74  var functionEls = document.querySelectorAll('.function');
75  for (var i = 0; i < functionEls.length; ++i) {
76    var visible = functionEls[i].id === divId;
77    if (functionEls[i].id === divId)
78      functionEls[i].removeAttribute('hidden');
79    else
80      functionEls[i].setAttribute('hidden', '');
81  }
82}
83
84function addNameToSelectElements(cssClass, handle, name) {
85  var text = '[' + handle + '] ' + name;
86  var selectEls = document.querySelectorAll(cssClass);
87  for (var i = 0; i < selectEls.length; ++i) {
88    var optionEl = document.createElement('option');
89    optionEl.setAttribute('value', handle);
90    optionEl.appendChild(document.createTextNode(text));
91    selectEls[i].appendChild(optionEl);
92  }
93}
94
95function removeNameFromSelectElements(cssClass, handle) {
96  var optionEls = document.querySelectorAll(cssClass + ' > option');
97  for (var i = 0; i < optionEls.length; ++i) {
98    var optionEl = optionEls[i];
99    if (optionEl.value == handle) {
100      var selectEl = optionEl.parentNode;
101      selectEl.removeChild(optionEl);
102    }
103  }
104}
105
106var filehandle_map = {};
107var dirhandle_map = {};
108
109function fopen(e) {
110  var filename = $('fopenFilename').value;
111  var access = $('fopenMode').value;
112  postCall('fopen', filename, access, function(filename, filehandle) {
113    filehandle_map[filehandle] = filename;
114
115    addNameToSelectElements('.file-handle', filehandle, filename);
116    common.logMessage('File ' + filename + ' opened successfully.');
117  });
118}
119
120function fclose(e) {
121  var filehandle = parseInt($('fcloseHandle').value, 10);
122  postCall('fclose', filehandle, function(filehandle) {
123    var filename = filehandle_map[filehandle];
124    removeNameFromSelectElements('.file-handle', filehandle, filename);
125    common.logMessage('File ' + filename + ' closed successfully.');
126  });
127}
128
129function fread(e) {
130  var filehandle = parseInt($('freadHandle').value, 10);
131  var numBytes = parseInt($('freadBytes').value, 10);
132  postCall('fread', filehandle, numBytes, function(filehandle, data) {
133    var filename = filehandle_map[filehandle];
134    common.logMessage('Read "' + data + '" from file ' + filename + '.');
135  });
136}
137
138function fwrite(e) {
139  var filehandle = parseInt($('fwriteHandle').value, 10);
140  var data = $('fwriteData').value;
141  postCall('fwrite', filehandle, data, function(filehandle, bytesWritten) {
142    var filename = filehandle_map[filehandle];
143    common.logMessage('Wrote ' + bytesWritten + ' bytes to file ' + filename +
144        '.');
145  });
146}
147
148function fseek(e) {
149  var filehandle = parseInt($('fseekHandle').value, 10);
150  var offset = parseInt($('fseekOffset').value, 10);
151  var whence = parseInt($('fseekWhence').value, 10);
152  postCall('fseek', filehandle, offset, whence, function(filehandle, filepos) {
153    var filename = filehandle_map[filehandle];
154    common.logMessage('Seeked to location ' + filepos + ' in file ' + filename +
155        '.');
156  });
157}
158
159function fflush(e) {
160  var filehandle = parseInt($('fflushHandle').value, 10);
161  postCall('fflush', filehandle, function(filehandle, filepos) {
162    var filename = filehandle_map[filehandle];
163    common.logMessage('flushed ' + filename + '.');
164  });
165}
166
167function stat(e) {
168  var filename = $('statFilename').value;
169  postCall('stat', filename, function(filename, size) {
170    common.logMessage('File ' + filename + ' has size ' + size + '.');
171  });
172}
173
174function opendir(e) {
175  var dirname = $('opendirDirname').value;
176  postCall('opendir', dirname, function(dirname, dirhandle) {
177    dirhandle_map[dirhandle] = dirname;
178
179    addNameToSelectElements('.dir-handle', dirhandle, dirname);
180    common.logMessage('Directory ' + dirname + ' opened successfully.');
181  });
182}
183
184function readdir(e) {
185  var dirhandle = parseInt($('readdirHandle').value, 10);
186  postCall('readdir', dirhandle, function(dirhandle, ino, name) {
187    var dirname = dirhandle_map[dirhandle];
188    if (ino === undefined) {
189      common.logMessage('End of directory.');
190    } else {
191      common.logMessage('Read entry ("' + name + '", ino = ' + ino +
192                        ') from directory ' + dirname + '.');
193    }
194  });
195}
196
197function closedir(e) {
198  var dirhandle = parseInt($('closedirHandle').value, 10);
199  postCall('closedir', dirhandle, function(dirhandle) {
200    var dirname = dirhandle_map[dirhandle];
201    delete dirhandle_map[dirhandle];
202
203    removeNameFromSelectElements('.dir-handle', dirhandle, dirname);
204    common.logMessage('Directory ' + dirname + ' closed successfully.');
205  });
206}
207
208function mkdir(e) {
209  var dirname = $('mkdirDirname').value;
210  var mode = parseInt($('mkdirMode').value, 10);
211  postCall('mkdir', dirname, mode, function(dirname) {
212    common.logMessage('Directory ' + dirname + ' created successfully.');
213  });
214}
215
216function rmdir(e) {
217  var dirname = $('rmdirDirname').value;
218  postCall('rmdir', dirname, function(dirname) {
219    common.logMessage('Directory ' + dirname + ' removed successfully.');
220  });
221}
222
223function chdir(e) {
224  var dirname = $('chdirDirname').value;
225  postCall('chdir', dirname, function(dirname) {
226    common.logMessage('Changed directory to: ' + dirname + '.');
227  });
228}
229
230function getcwd(e) {
231  postCall('getcwd', function(dirname) {
232    common.logMessage('getcwd: ' + dirname + '.');
233  });
234}
235
236function getaddrinfo(e) {
237  var name = $('getaddrinfoName').value;
238  var family = $('getaddrinfoFamily').value;
239  postCall('getaddrinfo', name, family, function(name, addrType) {
240    common.logMessage('getaddrinfo returned successfully');
241    common.logMessage('ai_cannonname = ' + name + '.');
242    var count = 1;
243    for (var i = 1; i < arguments.length; i+=2) {
244      var msg = 'Address number ' + count + ' = ' + arguments[i] +
245                ' (' + arguments[i+1] + ')';
246      common.logMessage(msg);
247      count += 1;
248    }
249  });
250}
251
252function gethostbyname(e) {
253  var name = $('gethostbynameName').value;
254  postCall('gethostbyname', name, function(name, addrType) {
255    common.logMessage('gethostbyname returned successfully');
256    common.logMessage('h_name = ' + name + '.');
257    common.logMessage('h_addr_type = ' + addrType + '.');
258    for (var i = 2; i < arguments.length; i++) {
259      common.logMessage('Address number ' + (i-1) + ' = ' + arguments[i] + '.');
260    }
261  });
262}
263
264function connect(e) {
265  var host = $('connectHost').value;
266  var port = parseInt($('connectPort').value, 10);
267  postCall('connect', host, port, function(sockhandle) {
268    common.logMessage('connected');
269    addNameToSelectElements('.sock-handle', sockhandle, '[socket]');
270  });
271}
272
273function recv(e) {
274  var handle = parseInt($('recvHandle').value, 10);
275  var bufferSize = parseInt($('recvBufferSize').value, 10);
276  postCall('recv', handle, bufferSize, function(messageLen, message) {
277    common.logMessage("received " + messageLen + ' bytes: ' + message);
278  });
279}
280
281function send(e) {
282  var handle = parseInt($('sendHandle').value, 10);
283  var message = $('sendMessage').value;
284  postCall('send', handle, message, function(sentBytes) {
285    common.logMessage("sent bytes: " + sentBytes);
286  });
287}
288
289function close(e) {
290  var handle = parseInt($('closeHandle').value, 10);
291  postCall('close', handle, function(sock) {
292    removeNameFromSelectElements('.sock-handle', sock, "[socket]");
293    common.logMessage("closed socket: " + sock);
294  });
295}
296
297var funcToCallback = {};
298
299function postCall(func) {
300  var callback = arguments[arguments.length - 1];
301  funcToCallback[func] = callback;
302
303  nacl_module.postMessage({
304    cmd: func,
305    args: Array.prototype.slice.call(arguments, 1, -1)
306  });
307}
308
309function ArrayBufferToString(buf) {
310  return String.fromCharCode.apply(null, new Uint16Array(buf));
311}
312
313// Called by the common.js module.
314function handleMessage(message_event) {
315  var data = message_event.data;
316  if ((typeof(data) === 'string' || data instanceof String)) {
317    common.logMessage(data);
318  } else if (data instanceof Object) {
319    var pipeName = data['pipe']
320    if (pipeName !== undefined) {
321      // Message for JavaScript I/O pipe
322      var operation = data['operation'];
323      if (operation == 'write') {
324        $('pipe_output').value += ArrayBufferToString(data['payload']);
325      } else if (operation == 'ack') {
326        common.logMessage(pipeName + ": ack:" + data['payload']);
327      } else {
328        common.logMessage('Got unexpected pipe operation: ' + operation);
329      }
330    } else {
331      // Result from a function call.
332      var params = data.args;
333      var funcName = data.cmd;
334      var callback = funcToCallback[funcName];
335
336      if (!callback) {
337        common.logMessage('Error: Bad message ' + funcName +
338                          ' received from NaCl module.');
339        return;
340      }
341
342      delete funcToCallback[funcName];
343      callback.apply(null, params);
344    }
345  } else {
346    common.logMessage('Error: Unknow message `' + data +
347                      '` received from NaCl module.');
348  }
349}
350