OLD | NEW |
1 /* | 1 /* |
2 * Copyright (c) 2014 The Native Client Authors. All rights reserved. | 2 * Copyright (c) 2014 The Native Client Authors. All rights reserved. |
3 * Use of this source code is governed by a BSD-style license that can be | 3 * Use of this source code is governed by a BSD-style license that can be |
4 * found in the LICENSE file. | 4 * found in the LICENSE file. |
5 */ | 5 */ |
6 | 6 |
7 /* jshint evil: true */ | 7 /* jshint evil: true */ |
8 /* globals PipeServer */ | 8 /* globals PipeServer */ |
9 | 9 |
10 'use strict'; | 10 'use strict'; |
(...skipping 19 matching lines...) Expand all Loading... |
30 mountPoint: undefined, | 30 mountPoint: undefined, |
31 | 31 |
32 // Mount is available | 32 // Mount is available |
33 available: false, | 33 available: false, |
34 | 34 |
35 // Mount has been mounted | 35 // Mount has been mounted |
36 mounted: false | 36 mounted: false |
37 }; | 37 }; |
38 | 38 |
39 /** | 39 /** |
| 40 * This creates a popup that runs a NaCl process inside. |
| 41 * |
| 42 * @param {Object} process The NaCl process to be run. |
| 43 * @param {number} width |
| 44 * @param {number} height |
| 45 * @param {string} title |
| 46 */ |
| 47 function GraphicalPopup(process, width, height, title) { |
| 48 this.process = process || null; |
| 49 this.width = width || GraphicalPopup.DEFAULT_WIDTH; |
| 50 this.height = height || GraphicalPopup.DEFAULT_HEIGHT; |
| 51 this.title = title || ''; |
| 52 this.win = null; |
| 53 this.onClosed = null; |
| 54 } |
| 55 |
| 56 /** |
| 57 * The default width of the popup. |
| 58 * @type {number} |
| 59 */ |
| 60 GraphicalPopup.DEFAULT_WIDTH = 600; |
| 61 |
| 62 /** |
| 63 * The default height of the popup. |
| 64 * @type {number} |
| 65 */ |
| 66 GraphicalPopup.DEFAULT_HEIGHT = 400; |
| 67 |
| 68 /** |
| 69 * The (empty) HTML file to which the NaCl module is added. |
| 70 * @const |
| 71 */ |
| 72 GraphicalPopup.HTML_FILE = 'graphical.html'; |
| 73 |
| 74 /** |
| 75 * Focus the window in which this code is run. |
| 76 */ |
| 77 GraphicalPopup.focusCurrentWindow = function () { |
| 78 chrome.app.window.current().focus(); |
| 79 }; |
| 80 |
| 81 /** |
| 82 * This callback is called when the popup is closed. |
| 83 * @callback closedCallback |
| 84 */ |
| 85 |
| 86 /** |
| 87 * Set a function to be called as a callback when the popup is closed. |
| 88 * @param {closedCallback} listener |
| 89 */ |
| 90 GraphicalPopup.prototype.setClosedListener = function (listener) { |
| 91 if (this.win) { |
| 92 throw new Error("Cannot set closed listener after creating window."); |
| 93 } |
| 94 this.onClosed = listener; |
| 95 }; |
| 96 |
| 97 /** |
| 98 * Create the window. |
| 99 */ |
| 100 GraphicalPopup.prototype.create = function () { |
| 101 var self = this; |
| 102 chrome.app.window.create('graphical.html', { |
| 103 'bounds': { |
| 104 'width': self.width, |
| 105 'height': self.height |
| 106 }, |
| 107 }, function (win) { |
| 108 var process = self.process; |
| 109 var popup = win.contentWindow; |
| 110 |
| 111 self.win = process.window = win; |
| 112 |
| 113 popup.document.title = self.title; |
| 114 |
| 115 popup.addEventListener('load', function () { |
| 116 process.style.position = 'absolute'; |
| 117 process.style.top = '0'; |
| 118 process.style.left = '0'; |
| 119 process.style.width = '100%'; |
| 120 process.style.height = '100%'; |
| 121 popup.document.body.appendChild(process); |
| 122 }); |
| 123 |
| 124 popup.focused = true; |
| 125 popup.addEventListener('focus', function () { |
| 126 this.focused = true; |
| 127 }); |
| 128 popup.addEventListener('blur', function () { |
| 129 this.focused = false; |
| 130 }); |
| 131 |
| 132 if (self.onClosed) { |
| 133 win.onClosed.addListener(self.onClosed); |
| 134 } |
| 135 }); |
| 136 }; |
| 137 |
| 138 /** |
| 139 * Close the popup. |
| 140 */ |
| 141 GraphicalPopup.prototype.destroy = function () { |
| 142 if (this.win.contentWindow.focused) { |
| 143 GraphicalPopup.focusCurrentWindow(); |
| 144 } |
| 145 if (this.onClosed) { |
| 146 this.win.onClosed.removeListener(this.onClosed); |
| 147 } |
| 148 this.win.close(); |
| 149 |
| 150 this.process = null; |
| 151 this.win = null; |
| 152 }; |
| 153 |
| 154 |
| 155 /** |
40 * NaClProcessManager provides a framework for NaCl executables to run within a | 156 * NaClProcessManager provides a framework for NaCl executables to run within a |
41 * web-based terminal. | 157 * web-based terminal. |
42 */ | 158 */ |
43 function NaClProcessManager() { | 159 function NaClProcessManager() { |
44 var self = this; | 160 var self = this; |
45 self.onError = function() {}; | 161 self.onError = null; |
46 self.onStdout = function() {}; | 162 self.onStdout = null; |
47 self.onRootProgress = function() {}; | 163 self.onRootProgress = null; |
48 self.onRootLoad = function() {}; | 164 self.onRootLoad = null; |
49 | 165 |
50 // The process which gets the input from the user. | 166 // The process which gets the input from the user. |
51 self.foregroundProcess = null; | 167 self.foregroundProcess = null; |
52 | 168 |
53 // Process group information keyed by PGID. The value is an object consisting | 169 // Process group information keyed by PGID. The value is an object consisting |
54 // of the fields: { | 170 // of the fields: { |
55 // sid: the session ID of the process group | 171 // sid: the session ID of the process group |
56 // processes: an object keyed by the PIDs of processes in this group, used | 172 // processes: an object keyed by the PIDs of processes in this group, used |
57 // as a set | 173 // as a set |
58 // } | 174 // } |
(...skipping 207 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
266 /** | 382 /** |
267 * Handles an architecture gotten event. | 383 * Handles an architecture gotten event. |
268 * @callback naclArchCallback | 384 * @callback naclArchCallback |
269 */ | 385 */ |
270 | 386 |
271 /** | 387 /** |
272 * Called to get the nacl architectgure. | 388 * Called to get the nacl architectgure. |
273 * @param {naclArchCallback} callback. | 389 * @param {naclArchCallback} callback. |
274 */ | 390 */ |
275 function getNaClArch(callback) { | 391 function getNaClArch(callback) { |
276 if (getNaClArch.naclArch_ === undefined) { | 392 if (getNaClArch.naclArch === undefined) { |
277 if (chrome && chrome.runtime && chrome.runtime.getPlatformInfo) { | 393 if (chrome && chrome.runtime && chrome.runtime.getPlatformInfo) { |
278 chrome.runtime.getPlatformInfo(function(platformInfo) { | 394 chrome.runtime.getPlatformInfo(function (platformInfo) { |
279 getNaClArch.naclArch_ = { | 395 getNaClArch.naclArch = { |
280 'x86-32': 'i686', | 396 'x86-32': 'i686', |
281 'x86-64': 'x86_64', | 397 'x86-64': 'x86_64', |
282 'arm': 'arm', | 398 'arm': 'arm', |
283 }[platformInfo.nacl_arch] || platformInfo.nacl_arch; | 399 }[platformInfo.nacl_arch] || platformInfo.nacl_arch; |
284 callback(getNaClArch.naclArch_); | 400 callback(getNaClArch.naclArch); |
285 }); | 401 }); |
286 return; | 402 return; |
287 } else { | |
288 getNaClArch.naclArch_ = null; | |
289 } | 403 } |
| 404 getNaClArch.naclArch = null; |
290 } | 405 } |
291 setTimeout(function() { | 406 setTimeout(function () { |
292 callback(getNaClArch.naclArch_); | 407 callback(getNaClArch.naclArch); |
293 }, 0); | 408 }, 0); |
294 } | 409 } |
295 | 410 |
296 /** | 411 /** |
297 * Handles a stdout event. | 412 * Handles a stdout event. |
298 * @callback stdoutCallback | 413 * @callback stdoutCallback |
299 * @param {string} msg The string sent to stdout. | 414 * @param {string} msg The string sent to stdout. |
300 */ | 415 */ |
301 | 416 |
302 /** | 417 /** |
303 * Listen for stdout from the spawned processes. | 418 * Listen for stdout from the spawned processes. |
304 * @param {stdoutCallback} callback The callback to be called on a stdout write. | 419 * @param {stdoutCallback} callback The callback to be called on a stdout write. |
305 */ | 420 */ |
306 NaClProcessManager.prototype.setStdoutListener = function(callback) { | 421 NaClProcessManager.prototype.setStdoutListener = function (callback) { |
307 this.onStdout = callback; | 422 this.onStdout = callback; |
308 }; | 423 }; |
309 | 424 |
310 /** | 425 /** |
311 * Handles an error event. | 426 * Handles an error event. |
312 * @callback errorCallback | 427 * @callback errorCallback |
313 * @param {string} cmd The name of the process with the error. | 428 * @param {string} cmd The name of the process with the error. |
314 * @param {string} err The error message. | 429 * @param {string} err The error message. |
315 */ | 430 */ |
316 | 431 |
317 /** | 432 /** |
318 * Listen for errors from the spawned processes. | 433 * Listen for errors from the spawned processes. |
319 * @param {errorCallback} callback The callback to be called on error. | 434 * @param {errorCallback} callback The callback to be called on error. |
320 */ | 435 */ |
321 NaClProcessManager.prototype.setErrorListener = function(callback) { | 436 NaClProcessManager.prototype.setErrorListener = function (callback) { |
322 this.onError = callback; | 437 this.onError = callback; |
323 }; | 438 }; |
324 | 439 |
325 /** | 440 /** |
326 * Handles a progress event from the root process. | 441 * Handles a progress event from the root process. |
327 * @callback rootProgressCallback | 442 * @callback rootProgressCallback |
328 * @param {string} url The URL that is being loaded. | 443 * @param {string} url The URL that is being loaded. |
329 * @param {boolean} lengthComputable Is our progress quantitatively measurable? | 444 * @param {boolean} lengthComputable Is our progress quantitatively measurable? |
330 * @param {number} loaded The number of bytes that have been loaded. | 445 * @param {number} loaded The number of bytes that have been loaded. |
331 * @param {number} total The total number of bytes to be loaded. | 446 * @param {number} total The total number of bytes to be loaded. |
332 */ | 447 */ |
333 | 448 |
334 /** | 449 /** |
335 * Listen for a progress event from the root process. | 450 * Listen for a progress event from the root process. |
336 * @param {rootProgressCallback} callback The callback to be called on progress. | 451 * @param {rootProgressCallback} callback The callback to be called on progress. |
337 */ | 452 */ |
338 NaClProcessManager.prototype.setRootProgressListener = function(callback) { | 453 NaClProcessManager.prototype.setRootProgressListener = function (callback) { |
339 this.onRootProgress = callback; | 454 this.onRootProgress = callback; |
340 }; | 455 }; |
341 | 456 |
342 /** | 457 /** |
343 * Handles a load event from the root process. | 458 * Handles a load event from the root process. |
344 * @callback rootLoadCallback | 459 * @callback rootLoadCallback |
345 */ | 460 */ |
346 | 461 |
347 /** | 462 /** |
348 * Listen for a load event from the root process. | 463 * Listen for a load event from the root process. |
349 * @param {rootLoadCallback} callback The callback to be called on load. | 464 * @param {rootLoadCallback} callback The callback to be called on load. |
350 */ | 465 */ |
351 NaClProcessManager.prototype.setRootLoadListener = function(callback) { | 466 NaClProcessManager.prototype.setRootLoadListener = function (callback) { |
352 this.onRootLoad = callback; | 467 this.onRootLoad = callback; |
353 }; | 468 }; |
354 | 469 |
355 /** | 470 /** |
356 * Is the given process the root process of this process manager? A root | 471 * Is the given process the root process of this process manager? A root |
357 * process is the one created by NaClProcessManager and not spawned by | 472 * process is the one created by NaClProcessManager and not spawned by |
358 * another process. | 473 * another process. |
359 * @param {HTMLObjectElement} process The element about which one is inquiring. | 474 * @param {HTMLObjectElement} process The element about which one is inquiring. |
360 */ | 475 */ |
361 NaClProcessManager.prototype.isRootProcess = function(process) { | 476 NaClProcessManager.prototype.isRootProcess = function (process) { |
362 return !process.parent; | 477 return !process.parent; |
363 }; | 478 }; |
364 | 479 |
365 /** | 480 /** |
366 * Broadcast message from javascript to all the processes. | 481 * Broadcast message from javascript to all the processes. |
367 * @message the message to be broadcasted | 482 * @message the message to be broadcasted |
368 * @callback callback to be stashed away and called when | 483 * @callback callback to be stashed away and called when |
369 * a process responds with a mount status update | 484 * a process responds with a mount status update |
370 */ | 485 */ |
371 NaClProcessManager.prototype.broadcastMessage = function(message, callback) { | 486 NaClProcessManager.prototype.broadcastMessage = function (message, callback) { |
372 this.mountUpdateCallback = callback; | 487 this.mountUpdateCallback = callback; |
373 for (var p in this.processes) { | 488 Object.keys(this.processes).forEach(function (key) { |
374 this.processes[p].domElement.postMessage(message); | 489 this.processes[key].domElement.postMessage(message); |
375 } | 490 }); |
376 }; | 491 }; |
377 | 492 |
378 /** | 493 /** |
379 * Sync Mount status every time a mount/unmount message | 494 * Sync Mount status every time a mount/unmount message |
380 * is recieved from a process. | 495 * is recieved from a process. |
381 */ | 496 */ |
382 NaClProcessManager.prototype.syncMountStatus_ = function() { | 497 NaClProcessManager.prototype.syncMountStatus_ = function () { |
383 var result = true; | 498 var result = true; |
384 | 499 |
385 if (g_mount.available) { | 500 if (g_mount.available) { |
386 for (var p in this.processes) { | 501 Object.keys(this.processes).forEach(function (p) { |
387 result = (result && this.processes[p].mounted); | 502 result = (result && this.processes[p].mounted); |
388 } | 503 }); |
389 } else { | 504 } else { |
390 result = false; | 505 result = false; |
391 for (var p in this.processes) { | 506 Object.keys(this.processes).forEach(function (p) { |
392 result = (result || this.processes[p].mounted); | 507 result = (result || this.processes[p].mounted); |
393 } | 508 }); |
394 } | 509 } |
395 | 510 |
396 g_mount.mounted = result; | 511 g_mount.mounted = result; |
397 if (this.mountUpdateCallback !== null) { | 512 if (this.mountUpdateCallback !== null) { |
398 this.mountUpdateCallback(); | 513 this.mountUpdateCallback(); |
399 } | 514 } |
400 return result; | 515 return result; |
401 }; | 516 }; |
402 | 517 |
403 /** | 518 /** |
404 * Makes the path in a NMF entry to fully specified path. | 519 * Makes the path in a NMF entry to fully specified path. |
405 * @private | 520 * @private |
406 */ | 521 */ |
407 NaClProcessManager.prototype.adjustNmfEntry_ = function(entry) { | 522 NaClProcessManager.prototype.adjustNmfEntry_ = function (entry) { |
408 for (var arch in entry) { | 523 Object.keys(entry).forEach(function (arch) { |
409 var path; | 524 var path; |
410 if (arch === 'portable') { | 525 if (arch === 'portable') { |
411 if (entry[arch]['pnacl-translate'] === undefined || | 526 if (entry[arch]['pnacl-translate'] === undefined || |
412 entry[arch]['pnacl-translate']['url'] === undefined) { | 527 entry[arch]['pnacl-translate'].url === undefined) { |
413 return; | 528 return; |
414 } | 529 } |
415 path = entry[arch]['pnacl-translate']['url']; | 530 path = entry[arch]['pnacl-translate'].url; |
416 } else { | 531 } else { |
417 if (entry[arch]['url'] === undefined) { | 532 if (entry[arch].url === undefined) { |
418 return; | 533 return; |
419 } | 534 } |
420 path = entry[arch]['url']; | 535 path = entry[arch].url; |
421 } | 536 } |
422 | 537 |
423 // Convert 'path' from the NaCl VFS into an HTML5 filesystem: URL | 538 // Convert 'path' from the NaCl VFS into an HTML5 filesystem: URL |
424 var tmpMountPoint = '/tmp/'; | 539 var tmpMountPoint = '/tmp/'; |
425 var httpMountPoint = '/mnt/http/'; | 540 var httpMountPoint = '/mnt/http/'; |
426 var fsname = '/persistent/'; | 541 var fsname = '/persistent/'; |
427 if (path.indexOf(tmpMountPoint) === 0) { | 542 if (path.indexOf(tmpMountPoint) === 0) { |
428 // Strip the /tmp/ prefix | 543 // Strip the /tmp/ prefix |
429 path = path.replace(tmpMountPoint, ''); | 544 path = path.replace(tmpMountPoint, ''); |
430 fsname = '/temporary/'; | 545 fsname = '/temporary/'; |
431 path = 'filesystem:' + location.origin + fsname + path; | 546 path = 'filesystem:' + location.origin + fsname + path; |
432 } else if (path.indexOf(httpMountPoint) === 0) { | 547 } else if (path.indexOf(httpMountPoint) === 0) { |
433 path = path.replace(httpMountPoint, ''); | 548 path = path.replace(httpMountPoint, ''); |
434 var base = location.href.match('.*/')[0]; | 549 var base = location.href.match('.*/')[0]; |
435 path = base + path; | 550 path = base + path; |
436 } else { | 551 } else { |
437 if (NaClProcessManager.fsroot !== undefined) { | 552 if (NaClProcessManager.fsroot !== undefined) { |
438 path = NaClProcessManager.fsroot + path; | 553 path = NaClProcessManager.fsroot + path; |
439 } | 554 } |
440 path = 'filesystem:' + location.origin + fsname + path; | 555 path = 'filesystem:' + location.origin + fsname + path; |
441 } | 556 } |
442 | 557 |
443 if (arch === 'portable') { | 558 if (arch === 'portable') { |
444 entry[arch]['pnacl-translate']['url'] = path; | 559 entry[arch]['pnacl-translate'].url = path; |
445 } else { | 560 } else { |
446 entry[arch]['url'] = path; | 561 entry[arch].url = path; |
447 } | 562 } |
448 } | 563 }); |
449 }; | 564 }; |
450 | 565 |
451 /** | 566 /** |
452 * Handle messages sent to us from NaCl. | 567 * Handle messages sent to us from NaCl. |
453 * @private | 568 * @private |
454 */ | 569 */ |
455 NaClProcessManager.prototype.handleMessage_ = function(e) { | 570 NaClProcessManager.prototype.handleMessage_ = function (e) { |
456 var msg = e.data; | 571 var msg = e.data; |
457 var src = e.srcElement; | 572 var src = e.srcElement; |
458 | 573 |
459 // Set handlers for commands. Each handler is passed three arguments: | 574 // Set handlers for commands. Each handler is passed three arguments: |
460 // msg: the data sent from the NaCl module | 575 // msg: the data sent from the NaCl module |
461 // reply: a callback to reply to the command | 576 // reply: a callback to reply to the command |
462 // src: the DOM element from which the message was received | 577 // src: the DOM element from which the message was received |
463 var handlers = { | 578 var handlers = { |
464 nacl_spawn: [this, this.handleMessageSpawn_], | 579 nacl_spawn: [this, this.handleMessageSpawn_], |
465 nacl_wait: [this, this.handleMessageWait_], | 580 nacl_wait: [this, this.handleMessageWait_], |
466 nacl_getpgid: [this, this.handleMessageGetPGID_], | 581 nacl_getpgid: [this, this.handleMessageGetPGID_], |
467 nacl_setpgid: [this, this.handleMessageSetPGID_], | 582 nacl_setpgid: [this, this.handleMessageSetPGID_], |
468 nacl_getsid: [this, this.handleMessageGetSID_], | 583 nacl_getsid: [this, this.handleMessageGetSID_], |
469 nacl_setsid: [this, this.handleMessageSetSID_], | 584 nacl_setsid: [this, this.handleMessageSetSID_], |
470 nacl_apipe: [this.pipeServer, this.pipeServer.handleMessageAPipe], | 585 nacl_apipe: [this.pipeServer, this.pipeServer.handleMessageAPipe], |
471 nacl_apipe_write: [this.pipeServer, | 586 nacl_apipe_write: [this.pipeServer, |
472 this.pipeServer.handleMessageAPipeWrite], | 587 this.pipeServer.handleMessageAPipeWrite], |
473 nacl_apipe_read: [this.pipeServer, | 588 nacl_apipe_read: [this.pipeServer, |
474 this.pipeServer.handleMessageAPipeRead], | 589 this.pipeServer.handleMessageAPipeRead], |
475 nacl_apipe_close: [this.pipeServer, | 590 nacl_apipe_close: [this.pipeServer, |
476 this.pipeServer.handleMessageAPipeClose], | 591 this.pipeServer.handleMessageAPipeClose], |
477 nacl_jseval: [this, this.handleMessageJSEval_], | 592 nacl_jseval: [this, this.handleMessageJSEval_], |
478 nacl_deadpid: [this, this.handleMessageDeadPid_], | 593 nacl_deadpid: [this, this.handleMessageDeadPid_], |
479 nacl_mountfs: [this,this.handleMessageMountFs_], | 594 nacl_mountfs: [this, this.handleMessageMountFs_], |
480 }; | 595 }; |
481 | 596 |
482 // TODO(channingh): Once pinned applications support "result" instead of | 597 // TODO(channingh): Once pinned applications support "result" instead of |
483 // "pid", change calls to reply() to set "result." | 598 // "pid", change calls to reply() to set "result." |
484 function reply(contents) { | 599 function reply(contents) { |
485 var message = {}; | 600 var message = {}; |
486 message[msg['id']] = contents; | 601 message[msg.id] = contents; |
487 // Enable to debug message stream (disabled for speed). | 602 // Enable to debug message stream (disabled for speed). |
488 // console.log(src.pid + '> reply: ' + JSON.stringify(reply)); | 603 // console.log(src.pid + '> reply: ' + JSON.stringify(reply)); |
489 src.postMessage(message); | 604 src.postMessage(message); |
490 } | 605 } |
491 | 606 |
492 if (msg['command'] && handlers[msg['command']]) { | 607 if (msg.command && handlers[msg.command]) { |
493 // Enable to debug message stream (disabled for speed). | 608 // Enable to debug message stream (disabled for speed). |
494 //console.log(src.pid + '> ' + msg['command'] + ': ' + JSON.stringify(msg)); | 609 //console.log(src.pid + '> ' + msg.command + ': ' + JSON.stringify(msg)); |
495 var handler = handlers[msg['command']]; | 610 var handler = handlers[msg.command]; |
496 handler[1].call(handler[0], msg, reply, src); | 611 handler[1].call(handler[0], msg, reply, src); |
497 } else if (msg['mount_status'] == 'success') { | 612 } else if (msg.mount_status === 'success') { |
498 // TODO(gdeepti): Remove monitoring mount status with strings. | 613 // TODO(gdeepti): Remove monitoring mount status with strings. |
499 this.processes[src.pid].mounted = true; | 614 this.processes[src.pid].mounted = true; |
500 this.syncMountStatus_(); | 615 this.syncMountStatus_(); |
501 } else if (msg['unmount_status'] == 'success') { | 616 } else if (msg.unmount_status === 'success') { |
502 this.processes[src.pid].mounted = false; | 617 this.processes[src.pid].mounted = false; |
503 this.syncMountStatus_(); | 618 this.syncMountStatus_(); |
504 } else if (msg['mount_status'] == 'fail' || msg['unmount_status'] == 'fail') { | 619 } else if (msg.mount_status === 'fail' || msg.unmount_status === 'fail') { |
505 console.log('mounter_status: ' + JSON.stringify(msg)); | 620 console.log('mounter_status: ' + JSON.stringify(msg)); |
506 } else if (typeof msg == 'string' && | 621 } else if (typeof msg === 'string' && |
507 msg.indexOf(NaClProcessManager.prefix) === 0) { | 622 msg.indexOf(NaClProcessManager.prefix) === 0) { |
508 var out = msg.substring(NaClProcessManager.prefix.length); | 623 var out = msg.substring(NaClProcessManager.prefix.length); |
509 this.onStdout(out); | 624 if (this.onStdout) { |
510 } else if (typeof msg == 'string' && | 625 this.onStdout(out); |
| 626 } |
| 627 } else if (typeof msg === 'string' && |
511 msg.indexOf('exited') === 0) { | 628 msg.indexOf('exited') === 0) { |
512 var exitCode = parseInt(msg.split(':', 2)[1]); | 629 var exitCode = parseInt(msg.split(':', 2)[1], 10); |
513 if (isNaN(exitCode)) | 630 if (isNaN(exitCode)) { |
514 exitCode = 0; | 631 exitCode = 0; |
| 632 } |
515 this.exit(exitCode, src); | 633 this.exit(exitCode, src); |
516 } else { | 634 } else { |
517 console.log('unexpected message: ' + JSON.stringify(msg)); | 635 console.log('unexpected message: ' + JSON.stringify(msg)); |
518 return; | 636 return; |
519 } | 637 } |
520 }; | 638 }; |
521 | 639 |
522 /** | 640 /** |
523 * Handle a nacl_spawn call. | 641 * Handle a nacl_spawn call. |
524 * @private | 642 * @private |
525 */ | 643 */ |
526 NaClProcessManager.prototype.handleMessageSpawn_ = function(msg, reply, src) { | 644 NaClProcessManager.prototype.handleMessageSpawn_ = function (msg, reply, src) { |
527 var self = this; | 645 var self = this; |
528 var args = msg['args']; | 646 var args = msg.args; |
529 var envs = msg['envs']; | 647 var envs = msg.envs; |
530 var cwd = msg['cwd']; | 648 var cwd = msg.cwd; |
531 var executable = args[0]; | 649 var executable = args[0]; |
532 var nmf = msg['nmf']; | 650 var nmf = msg.nmf; |
533 if (nmf) { | 651 if (nmf) { |
534 if (nmf['files']) { | 652 if (nmf.files) { |
535 for (var key in nmf['files']) | 653 Object.keys(nmf.files).forEach(function (key) { |
536 self.adjustNmfEntry_(nmf['files'][key]); | 654 self.adjustNmfEntry_(nmf.files[key]); |
| 655 }); |
537 } | 656 } |
538 self.adjustNmfEntry_(nmf['program']); | 657 self.adjustNmfEntry_(nmf.program); |
539 var blob = new Blob([JSON.stringify(nmf)], {type: 'text/plain'}); | 658 var blob = new Blob([JSON.stringify(nmf)], {type: 'text/plain'}); |
540 var nmfUrl = window.URL.createObjectURL(blob); | 659 var nmfUrl = window.URL.createObjectURL(blob); |
541 var naclType = self.checkNaClManifestType_(nmf) || 'nacl'; | 660 var naclType = self.checkNaClManifestType(nmf) || 'nacl'; |
542 self.spawn(nmfUrl, args, envs, cwd, naclType, src, function(pid) { | 661 self.spawn(nmfUrl, args, envs, cwd, naclType, src, function (pid) { |
543 reply({pid: pid}); | 662 reply({pid: pid}); |
544 }); | 663 }); |
545 } else { | 664 } else { |
546 if (NaClProcessManager.nmfWhitelist !== undefined && | 665 if (NaClProcessManager.nmfWhitelist !== undefined && |
547 NaClProcessManager.nmfWhitelist.indexOf(executable) === -1) { | 666 NaClProcessManager.nmfWhitelist.indexOf(executable) === -1) { |
548 var replyMsg = { | 667 var replyMsg = { |
549 pid: -Errno.ENOENT, | 668 pid: -Errno.ENOENT, |
550 }; | 669 }; |
551 console.log('nacl_spawn(error): ' + executable + ' not in whitelist'); | 670 console.log('nacl_spawn(error): ' + executable + ' not in whitelist'); |
552 reply(replyMsg); | 671 reply(replyMsg); |
553 return; | 672 return; |
554 } | 673 } |
555 nmf = executable + '.nmf'; | 674 nmf = executable + '.nmf'; |
556 self.checkUrlNaClManifestType(nmf, function(naclType) { | 675 self.checkUrlNaClManifestType(nmf, function (naclType) { |
557 self.spawn(nmf, args, envs, cwd, naclType, src, function(pid) { | 676 self.spawn(nmf, args, envs, cwd, naclType, src, function (pid) { |
558 reply({pid: pid}); | 677 reply({pid: pid}); |
559 }); | 678 }); |
560 }, function(msg) { | 679 }, function (msg) { |
561 var replyMsg = { | |
562 pid: -Errno.ENOENT, | |
563 }; | |
564 console.log('nacl_spawn(error): ' + msg); | 680 console.log('nacl_spawn(error): ' + msg); |
565 reply(replyMsg); | 681 reply({pid: -Errno.ENOENT}); |
566 }); | 682 }); |
567 } | 683 } |
568 }; | 684 }; |
569 | 685 |
570 | 686 |
571 /** | 687 /** |
572 * Handle a waitpid call. | 688 * Handle a waitpid call. |
573 * @private | 689 * @private |
574 */ | 690 */ |
575 NaClProcessManager.prototype.handleMessageWait_ = function(msg, reply, src) { | 691 NaClProcessManager.prototype.handleMessageWait_ = function (msg, reply, src) { |
576 this.waitpid(msg['pid'], msg['options'], function(pid, status) { | 692 this.waitpid(msg.pid, msg.options, function (pid, status) { |
577 reply({ | 693 reply({ |
578 pid: pid, | 694 pid: pid, |
579 status: status | 695 status: status |
580 }); | 696 }); |
581 }, src.pid); | 697 }, src.pid); |
582 }; | 698 }; |
583 | 699 |
584 /** | 700 /** |
585 * Handle a getpgid call. | 701 * Handle a getpgid call. |
586 * @private | 702 * @private |
587 */ | 703 */ |
588 NaClProcessManager.prototype.handleMessageGetPGID_ = function(msg, reply, src) { | 704 NaClProcessManager.prototype.handleMessageGetPGID_ = function (msg, reply, |
| 705 src) { |
589 var pgid; | 706 var pgid; |
590 var pid = parseInt(msg['pid']) || src.pid; | 707 var pid = parseInt(msg.pid, 10) || src.pid; |
591 | 708 |
592 if (pid < 0) { | 709 if (pid < 0) { |
593 pgid = -Errno.EINVAL; | 710 pgid = -Errno.EINVAL; |
594 } else if (!this.processes[pid] || this.processes[pid].exitCode !== null) { | 711 } else if (!this.processes[pid] || this.processes[pid].exitCode !== null) { |
595 pgid = -Errno.ESRCH; | 712 pgid = -Errno.ESRCH; |
596 } else { | 713 } else { |
597 pgid = this.processes[pid].pgid; | 714 pgid = this.processes[pid].pgid; |
598 } | 715 } |
599 reply({ | 716 reply({ |
600 pgid: pgid, | 717 pgid: pgid, |
601 }); | 718 }); |
602 }; | 719 }; |
603 | 720 |
604 /** | 721 /** |
605 * Handle a setpgid call. | 722 * Handle a setpgid call. |
606 * @private | 723 * @private |
607 */ | 724 */ |
608 NaClProcessManager.prototype.handleMessageSetPGID_ = function(msg, reply, src) { | 725 NaClProcessManager.prototype.handleMessageSetPGID_ = function (msg, reply, |
| 726 src) { |
609 var self = this; | 727 var self = this; |
610 function setpgid() { | 728 function setpgid() { |
611 var pid = parseInt(msg['pid']) || src.pid; | 729 var pid = parseInt(msg.pid, 10) || src.pid; |
612 var newPgid = parseInt(msg['pgid']) || pid; | 730 var newPgid = parseInt(msg.pgid, 10) || pid; |
613 | 731 |
614 if (newPgid < 0) { | 732 if (newPgid < 0) { |
615 return -Errno.EINVAL; | 733 return -Errno.EINVAL; |
616 } | 734 } |
617 if (!self.processes[pid] || self.processes[pid].exitCode !== null || | 735 if (!self.processes[pid] || self.processes[pid].exitCode !== null || |
618 (src.pid !== pid && src.pid !== self.processes[pid].ppid)) { | 736 (src.pid !== pid && src.pid !== self.processes[pid].ppid)) { |
619 return -Errno.ESRCH; | 737 return -Errno.ESRCH; |
620 } | 738 } |
621 | 739 |
622 var oldPgid = self.processes[pid].pgid; | 740 var oldPgid = self.processes[pid].pgid; |
(...skipping 12 matching lines...) Expand all Loading... |
635 // calling process. | 753 // calling process. |
636 if (sid !== callerSid) { | 754 if (sid !== callerSid) { |
637 return -Errno.EPERM; | 755 return -Errno.EPERM; |
638 } | 756 } |
639 | 757 |
640 // The target process is a session leader. | 758 // The target process is a session leader. |
641 if (sid === pid) { | 759 if (sid === pid) { |
642 return -Errno.EPERM; | 760 return -Errno.EPERM; |
643 } | 761 } |
644 | 762 |
645 self.deleteProcessFromGroup_(pid); | 763 self.deleteProcessFromGroup(pid); |
646 if (self.processGroups[newPgid]) { | 764 if (self.processGroups[newPgid]) { |
647 self.processGroups[newPgid].processes[pid] = true; | 765 self.processGroups[newPgid].processes[pid] = true; |
648 } else { | 766 } else { |
649 self.createProcessGroup_(newPgid, sid); | 767 self.createProcessGroup(newPgid, sid); |
650 } | 768 } |
651 self.processes[pid].pgid = newPgid; | 769 self.processes[pid].pgid = newPgid; |
652 return 0; | 770 return 0; |
653 } | 771 } |
654 reply({ | 772 reply({ |
655 result: setpgid(), | 773 result: setpgid(), |
656 }); | 774 }); |
657 }; | 775 }; |
658 | 776 |
659 /** | 777 /** |
660 * Handle a getsid call. | 778 * Handle a getsid call. |
661 * @private | 779 * @private |
662 */ | 780 */ |
663 NaClProcessManager.prototype.handleMessageGetSID_ = function(msg, reply, src) { | 781 NaClProcessManager.prototype.handleMessageGetSID_ = function (msg, reply, src) { |
664 var sid; | 782 var sid; |
665 var pid = parseInt(msg['pid']) || src.pid; | 783 var pid = parseInt(msg.pid, 10) || src.pid; |
666 var process = this.processes[pid]; | 784 var process = this.processes[pid]; |
667 | 785 |
668 if (!process || process.exitCode !== null) { | 786 if (!process || process.exitCode !== null) { |
669 sid = -Errno.ESRCH; | 787 sid = -Errno.ESRCH; |
670 } else { | 788 } else { |
671 sid = this.processGroups[process.pgid].sid; | 789 sid = this.processGroups[process.pgid].sid; |
672 } | 790 } |
673 reply({ | 791 reply({ |
674 sid: sid | 792 sid: sid |
675 }); | 793 }); |
676 }; | 794 }; |
677 | 795 |
678 /** | 796 /** |
679 * Handle a setsid call. | 797 * Handle a setsid call. |
680 * @private | 798 * @private |
681 */ | 799 */ |
682 NaClProcessManager.prototype.handleMessageSetSID_ = function(msg, reply, src) { | 800 NaClProcessManager.prototype.handleMessageSetSID_ = function (msg, reply, src) { |
683 var pid = src.pid; | 801 var pid = src.pid; |
684 var sid; | 802 var sid; |
685 | 803 |
686 // Setsid() cannot be called on a group leader. | 804 // Setsid() cannot be called on a group leader. |
687 if (this.processGroups[pid]) { | 805 if (this.processGroups[pid]) { |
688 sid = -Errno.EPERM; | 806 sid = -Errno.EPERM; |
689 } else { | 807 } else { |
690 this.deleteProcessFromGroup_(pid); | 808 this.deleteProcessFromGroup(pid); |
691 this.createProcessGroup_(pid, pid); | 809 this.createProcessGroup(pid, pid); |
692 sid = this.processes[pid].pgid = pid; | 810 sid = this.processes[pid].pgid = pid; |
693 } | 811 } |
694 | 812 |
695 reply({ | 813 reply({ |
696 sid: sid | 814 sid: sid |
697 }); | 815 }); |
698 }; | 816 }; |
699 | 817 |
700 /** | 818 /** |
701 * Handle a javascript invocation. | 819 * Handle a javascript invocation. |
702 * @private | 820 * @private |
703 */ | 821 */ |
704 NaClProcessManager.prototype.handleMessageJSEval_ = function(msg, reply) { | 822 NaClProcessManager.prototype.handleMessageJSEval_ = function (msg, reply) { |
705 // Using '' + so that undefined can be emitted as a string. | 823 // Using '' + so that undefined can be emitted as a string. |
706 reply({result: '' + eval(msg['cmd'])}); | 824 reply({result: String(eval(msg.cmd))}); |
707 }; | 825 }; |
708 | 826 |
709 /** | 827 /** |
710 * Create a process that immediately exits with a given status. | 828 * Create a process that immediately exits with a given status. |
711 * Used to implement vfork calling _exit. | 829 * Used to implement vfork calling _exit. |
712 * @private | 830 * @private |
713 */ | 831 */ |
714 NaClProcessManager.prototype.handleMessageDeadPid_ = function(msg, reply, src) { | 832 NaClProcessManager.prototype.handleMessageDeadPid_ = function (msg, reply, |
| 833 src) { |
715 var self = this; | 834 var self = this; |
716 // TODO(bradnelson): Avoid needing to frivolously manipulate the DOM to | 835 // TODO(bradnelson): Avoid needing to frivolously manipulate the DOM to |
717 // generate a dead pid by separating the process data structure manipulation | 836 // generate a dead pid by separating the process data structure manipulation |
718 // parts of spawn + exit from the DOM / module ones. | 837 // parts of spawn + exit from the DOM / module ones. |
719 self.spawn(null, [], [], '/', 'pnacl', src, function(pid, element) { | 838 self.spawn(null, [], [], '/', 'pnacl', src, function (pid, element) { |
720 self.exit(msg['status'], element); | 839 self.exit(msg.status, element); |
721 reply({pid: pid}); | 840 reply({pid: pid}); |
722 }); | 841 }); |
723 }; | 842 }; |
724 | 843 |
725 /** | 844 /** |
726 * Handle a mount filesystem call. | 845 * Handle a mount filesystem call. |
727 */ | 846 */ |
728 NaClProcessManager.prototype.handleMessageMountFs_ = function(msg, reply, src) { | 847 NaClProcessManager.prototype.handleMessageMountFs_ = function (msg, reply, |
| 848 src) { |
729 if (g_mount.available) { | 849 if (g_mount.available) { |
730 reply({ | 850 reply({ |
731 filesystem: g_mount.filesystem, | 851 filesystem: g_mount.filesystem, |
732 fullPath: g_mount.fullPath, | 852 fullPath: g_mount.fullPath, |
733 available: g_mount.available, | 853 available: g_mount.available, |
734 mountPoint: g_mount.mountPoint | 854 mountPoint: g_mount.mountPoint |
735 }); | 855 }); |
736 } else { | 856 } else { |
737 reply({ | 857 reply({ |
738 available: g_mount.available | 858 available: g_mount.available |
739 }); | 859 }); |
740 } | 860 } |
741 }; | 861 }; |
742 | 862 |
743 /** | 863 /** |
744 * Handle progress event from NaCl. | 864 * Handle progress event from NaCl. |
745 * @private | 865 * @private |
746 */ | 866 */ |
747 NaClProcessManager.prototype.handleProgress_ = function(e) { | 867 NaClProcessManager.prototype.handleProgress_ = function (e) { |
748 e.srcElement.moduleResponded = true; | 868 e.srcElement.moduleResponded = true; |
749 if (this.isRootProcess(e.srcElement)) { | 869 if (this.onRootProgress && this.isRootProcess(e.srcElement)) { |
750 this.onRootProgress(e.url, e.lengthComputable, e.loaded, e.total); | 870 this.onRootProgress(e.url, e.lengthComputable, e.loaded, e.total); |
751 } | 871 } |
752 }; | 872 }; |
753 | 873 |
754 /** | 874 /** |
755 * Handle load event from NaCl. | 875 * Handle load event from NaCl. |
756 * @private | 876 * @private |
757 */ | 877 */ |
758 NaClProcessManager.prototype.handleLoad_ = function(e) { | 878 NaClProcessManager.prototype.handleLoad_ = function (e) { |
759 e.srcElement.moduleResponded = true; | 879 e.srcElement.moduleResponded = true; |
760 if (this.isRootProcess(e.srcElement)) { | 880 if (this.onRootLoad && this.isRootProcess(e.srcElement)) { |
761 this.onRootLoad(); | 881 this.onRootLoad(); |
762 } | 882 } |
763 }; | 883 }; |
764 | 884 |
765 /** | 885 /** |
766 * Handle a timeout around module startup. | 886 * Handle a timeout around module startup. |
767 * @private | 887 * @private |
768 */ | 888 */ |
769 NaClProcessManager.prototype.handleStartupTimeout_ = function(src) { | 889 NaClProcessManager.prototype.handleStartupTimeout_ = function (src) { |
770 if (!src.moduleResponded) { | 890 if (!src.moduleResponded) { |
771 this.exit(NaClProcessManager.EX_NO_EXEC, src); | 891 this.exit(NaClProcessManager.EX_NO_EXEC, src); |
772 } | 892 } |
773 }; | 893 }; |
774 | 894 |
775 /** | 895 /** |
776 * Handle abort event from NaCl. | 896 * Handle abort event from NaCl. |
777 * @private | 897 * @private |
778 */ | 898 */ |
779 NaClProcessManager.prototype.handleLoadAbort_ = function(e) { | 899 NaClProcessManager.prototype.handleLoadAbort_ = function (e) { |
780 e.srcElement.moduleResponded = true; | 900 e.srcElement.moduleResponded = true; |
781 this.exit(NaClProcessManager.EXIT_CODE_KILL, e.srcElement); | 901 this.exit(NaClProcessManager.EXIT_CODE_KILL, e.srcElement); |
782 }; | 902 }; |
783 | 903 |
784 /** | 904 /** |
785 * Handle load error event from NaCl. | 905 * Handle load error event from NaCl. |
786 * @private | 906 * @private |
787 */ | 907 */ |
788 NaClProcessManager.prototype.handleLoadError_ = function(e) { | 908 NaClProcessManager.prototype.handleLoadError_ = function (e) { |
789 e.srcElement.moduleResponded = true; | 909 e.srcElement.moduleResponded = true; |
790 this.onError(e.srcElement.commandName, e.srcElement.lastError); | 910 if (this.onError) { |
| 911 this.onError(e.srcElement.commandName, e.srcElement.lastError); |
| 912 } |
791 this.exit(NaClProcessManager.EX_NO_EXEC, e.srcElement); | 913 this.exit(NaClProcessManager.EX_NO_EXEC, e.srcElement); |
792 }; | 914 }; |
793 | 915 |
794 /** | 916 /** |
795 * Handle crash event from NaCl. | 917 * Handle crash event from NaCl. |
796 * @private | 918 * @private |
797 */ | 919 */ |
798 NaClProcessManager.prototype.handleCrash_ = function(e) { | 920 NaClProcessManager.prototype.handleCrash_ = function (e) { |
799 e.srcElement.moduleResponded = true; | 921 e.srcElement.moduleResponded = true; |
800 this.exit(e.srcElement.exitStatus, e.srcElement); | 922 this.exit(e.srcElement.exitStatus, e.srcElement); |
801 }; | 923 }; |
802 | 924 |
803 /** | 925 /** |
804 * Create a process group with the given process as the leader and add the | 926 * Create a process group with the given process as the leader and add the |
805 * group to the table. | 927 * group to the table. |
806 * @private | 928 * @private |
807 * @param {number} pid The process that will be the leader. | 929 * @param {number} pid The process that will be the leader. |
808 * @param {number} sid The session ID of the session to which the process | 930 * @param {number} sid The session ID of the session to which the process |
809 * group belongs. | 931 * group belongs. |
810 */ | 932 */ |
811 NaClProcessManager.prototype.createProcessGroup_ = function(pid, sid) { | 933 NaClProcessManager.prototype.createProcessGroup = function (pid, sid) { |
812 if (this.processGroups[pid] !== undefined) { | 934 if (this.processGroups[pid] !== undefined) { |
813 throw new Error('createProcessGroup_(): process group already exists'); | 935 throw new Error('createProcessGroup(): process group already exists'); |
814 } | 936 } |
815 this.processGroups[pid] = { | 937 this.processGroups[pid] = { |
816 sid: sid, | 938 sid: sid, |
817 processes: {} | 939 processes: {} |
818 }; | 940 }; |
819 this.processGroups[pid].processes[pid] = true; | 941 this.processGroups[pid].processes[pid] = true; |
820 }; | 942 }; |
821 | 943 |
822 /** | 944 /** |
823 * Delete a process from the process group table. If a group has no more | 945 * Delete a process from the process group table. If a group has no more |
824 * processes, delete the group as well. | 946 * processes, delete the group as well. |
825 * @private | 947 * @private |
826 * @param {number} pid The process to be deleted. | 948 * @param {number} pid The process to be deleted. |
827 */ | 949 */ |
828 NaClProcessManager.prototype.deleteProcessFromGroup_ = function(pid) { | 950 NaClProcessManager.prototype.deleteProcessFromGroup = function (pid) { |
829 if (this.processes[pid] === undefined) { | 951 if (this.processes[pid] === undefined) { |
830 throw new Error('deleteProcessFromGroup_(): process does not exist'); | 952 throw new Error('deleteProcessFromGroup(): process does not exist'); |
831 } | 953 } |
832 var pgid = this.processes[pid].pgid; | 954 var pgid = this.processes[pid].pgid; |
833 if (this.processGroups[pgid] === undefined || | 955 if (this.processGroups[pgid] === undefined || |
834 this.processGroups[pgid].processes[pid] === undefined) { | 956 this.processGroups[pgid].processes[pid] === undefined) { |
835 throw new Error('deleteProcessFromGroup_(): process group not found'); | 957 throw new Error('deleteProcessFromGroup(): process group not found'); |
836 } | 958 } |
837 delete this.processGroups[pgid].processes[pid]; | 959 delete this.processGroups[pgid].processes[pid]; |
838 if (Object.keys(this.processGroups[pgid].processes).length === 0) { | 960 if (Object.keys(this.processGroups[pgid].processes).length === 0) { |
839 delete this.processGroups[pgid]; | 961 delete this.processGroups[pgid]; |
840 } | 962 } |
841 }; | 963 }; |
842 | 964 |
843 /** | 965 /** |
844 * Remove entries for a process from the process table. | 966 * Remove entries for a process from the process table. |
845 * @private | 967 * @private |
846 */ | 968 */ |
847 NaClProcessManager.prototype.deleteProcessEntry_ = function(pid) { | 969 NaClProcessManager.prototype.deleteProcessEntry = function (pid) { |
848 delete this.processes[pid]; | 970 delete this.processes[pid]; |
849 }; | 971 }; |
850 | 972 |
851 /** | 973 /** |
852 * Exit the command. | 974 * Exit the command. |
853 * @param {number} code The exit code of the process. | 975 * @param {number} code The exit code of the process. |
854 * @param {HTMLObjectElement} element The HTML element of the exited process. | 976 * @param {HTMLObjectElement} element The HTML element of the exited process. |
855 */ | 977 */ |
856 NaClProcessManager.prototype.exit = function(code, element) { | 978 NaClProcessManager.prototype.exit = function (code, element) { |
857 var pid = element.pid; | 979 var pid = element.pid; |
858 var ppid = this.processes[pid].ppid; | 980 var ppid = this.processes[pid].ppid; |
859 var pgid = this.processes[pid].pgid; | 981 var pgid = this.processes[pid].pgid; |
860 | 982 |
861 this.pipeServer.deleteProcess(pid); | 983 this.pipeServer.deleteProcess(pid); |
862 this.deleteProcessFromGroup_(pid); | 984 this.deleteProcessFromGroup(pid); |
863 | 985 |
864 // Reply to processes waiting on the exited process. | 986 // Reply to processes waiting on the exited process. |
865 var waitersToCheck = [pid, -1, -pgid]; | |
866 var reaped = false; | 987 var reaped = false; |
867 var waiters = this.waiters; | 988 var waiters = this.waiters; |
868 waitersToCheck.forEach(function(currPid) { | 989 [pid, -1, -pgid].forEach(function (currPid) { |
869 if (waiters[currPid] === undefined) { | 990 if (waiters[currPid] === undefined) { |
870 return; | 991 return; |
871 } | 992 } |
872 var currPidWaiters = waiters[currPid]; | 993 waiters[currPid].forEach(function (waiter) { |
873 for (var j = 0; j < currPidWaiters.length; j++) { | |
874 var waiter = currPidWaiters[j]; | |
875 if (waiter.srcPid === ppid) { | 994 if (waiter.srcPid === ppid) { |
876 waiter.reply(pid, code); | 995 waiter.reply(pid, code); |
877 reaped = true; | 996 reaped = true; |
878 } | 997 } |
879 } | 998 }); |
880 waiters[currPid] = currPidWaiters.filter(function(waiter) { | 999 waiters[currPid] = waiters[currPid].filter(function (waiter) { |
881 return waiter.srcPid !== ppid; | 1000 return waiter.srcPid !== ppid; |
882 }); | 1001 }); |
883 if (waiters[currPid].length === 0) { | 1002 if (waiters[currPid].length === 0) { |
884 delete waiters[currPid]; | 1003 delete waiters[currPid]; |
885 } | 1004 } |
886 }); | 1005 }); |
887 if (reaped) { | 1006 if (reaped) { |
888 this.deleteProcessEntry_(pid); | 1007 this.deleteProcessEntry(pid); |
889 } else { | 1008 } else { |
890 this.processes[pid].exitCode = code; | 1009 this.processes[pid].exitCode = code; |
891 } | 1010 } |
892 | 1011 |
893 // Mark as terminated. | 1012 // Mark as terminated. |
894 element.pid = -1; | 1013 element.pid = -1; |
895 | 1014 |
896 // Clean up HTML elements. | 1015 // Clean up HTML elements. |
897 if (element.parentNode === document.body) { | 1016 if (element.parentNode === document.body) { |
898 document.body.removeChild(element); | 1017 document.body.removeChild(element); |
899 } | 1018 } |
900 | 1019 |
901 if (element.popup) { | 1020 if (element.popup) { |
902 element.popup.destroy(); | 1021 element.popup.destroy(); |
903 } | 1022 } |
904 | 1023 |
905 var nextForegroundProcess = null; | 1024 var nextForegroundProcess = null; |
906 if (this.foregroundProcess === element) { | 1025 if (this.foregroundProcess === element) { |
907 nextForegroundProcess = element.parent; | 1026 nextForegroundProcess = element.parent; |
908 // When the parent has already finished, give the control to the | 1027 // When the parent has already finished, give the control to the |
909 // grand parent. | 1028 // grand parent. |
910 while (nextForegroundProcess && nextForegroundProcess.pid === -1) | 1029 while (nextForegroundProcess && nextForegroundProcess.pid === -1) { |
911 nextForegroundProcess = nextForegroundProcess.parent; | 1030 nextForegroundProcess = nextForegroundProcess.parent; |
| 1031 } |
912 | 1032 |
913 this.foregroundProcess = nextForegroundProcess; | 1033 this.foregroundProcess = nextForegroundProcess; |
914 } | 1034 } |
915 }; | 1035 }; |
916 | 1036 |
917 /** | 1037 /** |
918 * Determines if an object is a nacl or pnacl manifest. | 1038 * Determines if an object is a nacl or pnacl manifest. |
919 * | 1039 * |
920 * @param {object} manifest A manifest to examine. | 1040 * @param {object} manifest A manifest to examine. |
921 * @return {string} 'nacl' or 'pnacl' on success, or null on failure.. | 1041 * @return {string} 'nacl' or 'pnacl' on success, or null on failure.. |
922 */ | 1042 */ |
923 NaClProcessManager.prototype.checkNaClManifestType_ = function(manifest) { | 1043 NaClProcessManager.prototype.checkNaClManifestType = function (manifest) { |
924 if ('program' in manifest) { | 1044 if (manifest.hasOwnProperty('program')) { |
925 if ('portable' in manifest.program && | 1045 if (manifest.program.hasOwnProperty('portable') && |
926 'pnacl-translate' in manifest.program.portable) { | 1046 manifest.program.portable.hasOwnProperty('pnacl-translate')) { |
927 return 'pnacl'; | 1047 return 'pnacl'; |
928 } else { | |
929 return 'nacl'; | |
930 } | 1048 } |
931 } else { | 1049 return 'nacl'; |
932 return null; | |
933 } | 1050 } |
| 1051 return null; |
934 }; | 1052 }; |
935 | 1053 |
936 /** | 1054 /** |
937 * Determines if an url points to a nacl or pnacl manifest. | 1055 * Determines if an url points to a nacl or pnacl manifest. |
938 * | 1056 * |
939 * @param {string} url The url to download and parse. | 1057 * @param {string} url The url to download and parse. |
940 * @callback typeCallback Called with 'nacl' or 'pnacl' on success. | 1058 * @callback typeCallback Called with 'nacl' or 'pnacl' on success. |
941 * @callback errorCallback Called with error message on failure. | 1059 * @callback errorCallback Called with error message on failure. |
942 */ | 1060 */ |
943 NaClProcessManager.prototype.checkUrlNaClManifestType = function( | 1061 NaClProcessManager.prototype.checkUrlNaClManifestType = function ( |
944 url, typeCallback, errorCallback) { | 1062 url, typeCallback, errorCallback) { |
945 var self = this; | 1063 var self = this; |
946 var request = new XMLHttpRequest(); | 1064 var request = new XMLHttpRequest(); |
947 request.open('GET', url, true); | 1065 request.open('GET', url, true); |
948 request.onload = function() { | 1066 request.onload = function () { |
949 var manifest; | 1067 var manifest; |
950 try { | 1068 try { |
951 manifest = JSON.parse(request.responseText); | 1069 manifest = JSON.parse(request.responseText); |
952 } catch(e) { | 1070 } catch (e) { |
953 errorCallback('NaCl Manifest is not valid JSON at ' + url); | 1071 errorCallback('NaCl Manifest is not valid JSON at ' + url); |
954 return; | 1072 return; |
955 } | 1073 } |
956 var kind = self.checkNaClManifestType_(manifest); | 1074 var kind = self.checkNaClManifestType(manifest); |
957 if (kind === null) { | 1075 if (kind === null) { |
958 errorCallback('NaCl Manifest has bad format at ' + url); | 1076 errorCallback('NaCl Manifest has bad format at ' + url); |
959 } else { | 1077 } else { |
960 typeCallback(kind); | 1078 typeCallback(kind); |
961 } | 1079 } |
962 }; | 1080 }; |
963 request.onerror = function() { | 1081 request.onerror = function () { |
964 errorCallback('NaCl Manifest is unreachable at ' + url); | 1082 errorCallback('NaCl Manifest is unreachable at ' + url); |
965 }; | 1083 }; |
966 request.send(); | 1084 request.send(); |
967 }; | 1085 }; |
968 | 1086 |
969 /** | 1087 /** |
970 * Called once a pid / error code is known for a spawned process. | 1088 * Called once a pid / error code is known for a spawned process. |
971 * @callback spawnCallback | 1089 * @callback spawnCallback |
972 * @param {pid} pid The pid of the spawned process or error code if negative. | 1090 * @param {pid} pid The pid of the spawned process or error code if negative. |
973 * @param {HTMLObjectElement} element DOM object corresponding to the spawned | 1091 * @param {HTMLObjectElement} element DOM object corresponding to the spawned |
(...skipping 10 matching lines...) Expand all Loading... |
984 * @param {Array.<string>} envs The environment variables that the spawned | 1102 * @param {Array.<string>} envs The environment variables that the spawned |
985 * process can access. Each entry in the array should be of the format | 1103 * process can access. Each entry in the array should be of the format |
986 * "VARIABLE_NAME=value". | 1104 * "VARIABLE_NAME=value". |
987 * @param {string} cwd The current working directory. | 1105 * @param {string} cwd The current working directory. |
988 * @param {string} naclType 'nacl' or 'pnacl' to select the mime-type. | 1106 * @param {string} naclType 'nacl' or 'pnacl' to select the mime-type. |
989 * @param {HTMLObjectElement} parent The DOM object that corresponds to | 1107 * @param {HTMLObjectElement} parent The DOM object that corresponds to |
990 * the process that initiated the spawn. Set to null if there is no such | 1108 * the process that initiated the spawn. Set to null if there is no such |
991 * process. | 1109 * process. |
992 * @param {spawnCallback} callback. | 1110 * @param {spawnCallback} callback. |
993 */ | 1111 */ |
994 NaClProcessManager.prototype.spawn = function( | 1112 NaClProcessManager.prototype.spawn = function ( |
995 nmf, argv, envs, cwd, naclType, parent, callback) { | 1113 nmf, argv, envs, cwd, naclType, parent, callback) { |
996 var self = this; | 1114 var self = this; |
997 | 1115 |
998 getNaClArch(function(arch) { | 1116 getNaClArch(function (arch) { |
999 var mimetype = 'application/x-' + naclType; | 1117 var mimetype = 'application/x-' + naclType; |
1000 if (navigator.mimeTypes[mimetype] === undefined) { | 1118 if (navigator.mimeTypes[mimetype] === undefined) { |
1001 if (mimetype.indexOf('pnacl') != -1) { | 1119 if (mimetype.indexOf('pnacl') !== -1) { |
1002 console.log( | 1120 console.log( |
1003 'Browser does not support PNaCl or PNaCl is disabled.'); | 1121 'Browser does not support PNaCl or PNaCl is disabled.' |
| 1122 ); |
1004 } else { | 1123 } else { |
1005 console.log( | 1124 console.log( |
1006 'Browser does not support NaCl or NaCl is disabled.'); | 1125 'Browser does not support NaCl or NaCl is disabled.' |
| 1126 ); |
1007 } | 1127 } |
1008 callback(-Errno.ENOEXEC); | 1128 callback(-Errno.ENOEXEC); |
1009 return; | 1129 return; |
1010 } | 1130 } |
1011 | 1131 |
1012 if (self.ttyWidth === null || self.ttyHeight === null) { | 1132 if (self.ttyWidth === null || self.ttyHeight === null) { |
1013 console.log('tty size not set! Call onTerminalResize.'); | 1133 console.log('tty size not set! Call onTerminalResize.'); |
1014 callback(-Errno.ENOEXEC); | 1134 callback(-Errno.ENOEXEC); |
1015 return; | 1135 return; |
1016 } | 1136 } |
1017 | 1137 |
1018 var fg = document.createElement('object'); | 1138 var fg = document.createElement('object'); |
1019 self.foregroundProcess = fg; | 1139 self.foregroundProcess = fg; |
1020 | 1140 |
1021 fg.pid = self.pid; | 1141 fg.pid = self.pid; |
1022 ++self.pid; | 1142 self.pid += 1; |
1023 | 1143 |
1024 fg.width = 0; | 1144 fg.width = 0; |
1025 fg.height = 0; | 1145 fg.height = 0; |
1026 fg.data = nmf; | 1146 fg.data = nmf; |
1027 fg.type = mimetype; | 1147 fg.type = mimetype; |
1028 fg.parent = parent; | 1148 fg.parent = parent; |
1029 fg.commandName = argv[0]; | 1149 fg.commandName = argv[0]; |
1030 | 1150 |
1031 if (nmf !== null) { | 1151 if (nmf !== null) { |
1032 fg.addEventListener('abort', self.handleLoadAbort_.bind(self)); | 1152 fg.addEventListener('abort', self.handleLoadAbort_.bind(self)); |
1033 fg.addEventListener('crash', self.handleCrash_.bind(self)); | 1153 fg.addEventListener('crash', self.handleCrash_.bind(self)); |
1034 fg.addEventListener('error', self.handleLoadError_.bind(self)); | 1154 fg.addEventListener('error', self.handleLoadError_.bind(self)); |
1035 fg.addEventListener('load', self.handleLoad_.bind(self)); | 1155 fg.addEventListener('load', self.handleLoad_.bind(self)); |
1036 fg.addEventListener('message', self.handleMessage_.bind(self)); | 1156 fg.addEventListener('message', self.handleMessage_.bind(self)); |
1037 fg.addEventListener('progress', self.handleProgress_.bind(self)); | 1157 fg.addEventListener('progress', self.handleProgress_.bind(self)); |
1038 } | 1158 } |
1039 | 1159 |
1040 var pgid = parent ? self.processes[parent.pid].pgid : fg.pid; | 1160 var pgid = parent ? self.processes[parent.pid].pgid : fg.pid; |
1041 var ppid = parent ? parent.pid : NaClProcessManager.INIT_PID; | 1161 var ppid = parent ? parent.pid : NaClProcessManager.INIT_PID; |
1042 self.processes[fg.pid] = { | 1162 self.processes[fg.pid] = { |
1043 domElement: fg, | 1163 domElement: fg, |
1044 exitCode: null, | 1164 exitCode: null, |
1045 pgid: pgid, | 1165 pgid: pgid, |
1046 ppid: ppid, | 1166 ppid: ppid, |
1047 mounted: false, | 1167 mounted: false, |
1048 }; | 1168 }; |
1049 if (!parent) { | 1169 if (!parent) { |
1050 self.createProcessGroup_(fg.pid, fg.pid); | 1170 self.createProcessGroup(fg.pid, fg.pid); |
1051 } | 1171 } |
1052 self.processGroups[pgid].processes[fg.pid] = true; | 1172 self.processGroups[pgid].processes[fg.pid] = true; |
1053 | 1173 |
1054 var params = {}; | 1174 var params = {}; |
1055 // Default environment variables (can be overridden by envs) | 1175 // Default environment variables (can be overridden by envs) |
1056 params['PS_VERBOSITY'] = '2'; | 1176 params.PS_VERBOSITY = '2'; |
1057 params['TERM'] = 'xterm-256color'; | 1177 params.TERM = 'xterm-256color'; |
1058 params['PS_STDIN'] = '/dev/tty'; | 1178 params.PS_STDIN = '/dev/tty'; |
1059 params['PS_STDOUT'] = '/dev/tty'; | 1179 params.PS_STDOUT = '/dev/tty'; |
1060 params['PS_STDERR'] = '/dev/tty'; | 1180 params.PS_STDERR = '/dev/tty'; |
1061 | 1181 |
1062 for (var i = 0; i < envs.length; i++) { | 1182 envs.forEach(function (env) { |
1063 var env = envs[i]; | |
1064 var index = env.indexOf('='); | 1183 var index = env.indexOf('='); |
1065 if (index < 0) { | 1184 if (index < 0) { |
1066 console.error('Broken env: ' + env); | 1185 console.error('Broken env: ' + env); |
1067 continue; | 1186 return; |
1068 } | 1187 } |
1069 var key = env.substring(0, index); | 1188 var key = env.substring(0, index); |
1070 if (key === 'SRC' || key === 'DATA' || key.match(/^ARG\d+$/i)) | 1189 if (key === 'SRC' || key === 'DATA' || key.match(/^ARG\d+$/i)) { |
1071 continue; | 1190 return; |
| 1191 } |
1072 params[key] = env.substring(index + 1); | 1192 params[key] = env.substring(index + 1); |
1073 } | 1193 }); |
1074 | 1194 |
1075 // Addition environment variables (these override the incoming env) | 1195 // Addition environment variables (these override the incoming env) |
1076 params['PS_TTY_PREFIX'] = NaClProcessManager.prefix; | 1196 params.PS_TTY_PREFIX = NaClProcessManager.prefix; |
1077 params['PS_TTY_RESIZE'] = 'tty_resize'; | 1197 params.PS_TTY_RESIZE = 'tty_resize'; |
1078 params['PS_TTY_COLS'] = self.ttyWidth; | 1198 params.PS_TTY_COLS = self.ttyWidth; |
1079 params['PS_TTY_ROWS'] = self.ttyHeight; | 1199 params.PS_TTY_ROWS = self.ttyHeight; |
1080 params['PS_EXIT_MESSAGE'] = 'exited'; | 1200 params.PS_EXIT_MESSAGE = 'exited'; |
1081 params['LOCATION_ORIGIN'] = location.origin; | 1201 params.LOCATION_ORIGIN = location.origin; |
1082 params['PWD'] = cwd; | 1202 params.PWD = cwd; |
1083 params['NACL_PROCESS'] = '1'; | 1203 params.NACL_PROCESS = '1'; |
1084 params['NACL_PID'] = fg.pid; | 1204 params.NACL_PID = fg.pid; |
1085 params['NACL_PPID'] = ppid; | 1205 params.NACL_PPID = ppid; |
1086 if (NaClProcessManager.fsroot !== undefined) { | 1206 if (NaClProcessManager.fsroot !== undefined) { |
1087 params['NACL_HTML5_ROOT'] = NaClProcessManager.fsroot; | 1207 params.NACL_HTML5_ROOT = NaClProcessManager.fsroot; |
1088 } | 1208 } |
1089 | 1209 |
1090 if (chrome && chrome.runtime && chrome.runtime.getPlatformInfo) { | 1210 if (chrome && chrome.runtime && chrome.runtime.getPlatformInfo) { |
1091 if (arch === null) { | 1211 if (arch === null) { |
1092 console.log( | 1212 console.log( |
1093 'Browser does not support NaCl/PNaCl or is disabled.'); | 1213 'Browser does not support NaCl/PNaCl or is disabled.' |
| 1214 ); |
1094 callback(-Errno.ENOEXEC, fg); | 1215 callback(-Errno.ENOEXEC, fg); |
1095 return; | 1216 return; |
1096 } | 1217 } |
1097 params['NACL_ARCH'] = arch; | 1218 params.NACL_ARCH = arch; |
1098 } | 1219 } |
1099 | 1220 |
1100 function addParam(name, value) { | 1221 function addParam(name, value) { |
1101 // Don't set a 'type' field as self seems to confuse manifest parsing for | 1222 // Don't set a 'type' field as self seems to confuse manifest parsing for |
1102 // pnacl. | 1223 // pnacl. |
1103 if (name.toLowerCase() === 'type') { | 1224 if (name.toLowerCase() === 'type') { |
1104 return; | 1225 return; |
1105 } | 1226 } |
1106 var param = document.createElement('param'); | 1227 var param = document.createElement('param'); |
1107 param.name = name; | 1228 param.name = name; |
1108 param.value = value; | 1229 param.value = value; |
1109 fg.appendChild(param); | 1230 fg.appendChild(param); |
1110 } | 1231 } |
1111 | 1232 |
1112 for (var key in params) { | 1233 Object.keys(params).forEach(function (key) { |
1113 addParam(key, params[key]); | 1234 addParam(key, params[key]); |
1114 } | 1235 }); |
1115 | 1236 |
1116 // Add ARGV arguments from query parameters. | 1237 // Add ARGV arguments from query parameters. |
1117 function parseQuery(query) { | 1238 function parseQuery(query) { |
1118 if (query.charAt(0) === '?') { | 1239 if (query.charAt(0) === '?') { |
1119 query = query.substring(1); | 1240 query = query.substring(1); |
1120 } | 1241 } |
1121 var splitArgs = query.split('&'); | 1242 var splitArgs = query.split('&'); |
1122 var args = {}; | 1243 var args = {}; |
1123 for (var i = 0; i < splitArgs.length; i++) { | 1244 splitArgs.forEach(function (value) { |
1124 var keyValue = splitArgs[i].split('='); | 1245 var keyValue = value.split('='); |
1125 args[decodeURIComponent(keyValue[0])] = | 1246 args[decodeURIComponent(keyValue[0])] = |
1126 decodeURIComponent(keyValue[1]); | 1247 decodeURIComponent(keyValue[1]); |
1127 } | 1248 }); |
1128 return args; | 1249 return args; |
1129 } | 1250 } |
| 1251 |
1130 var args = parseQuery(document.location.search); | 1252 var args = parseQuery(document.location.search); |
1131 for (var argname in args) { | 1253 Object.keys(args).forEach(function (argname) { |
1132 addParam(argname, args[argname]); | 1254 addParam(argname, args[argname]); |
1133 } | 1255 }); |
1134 | 1256 |
1135 // If the application has set NaClTerm.argv and there were | 1257 // If the application has set NaClTerm.argv and there were |
1136 // no arguments set in the query parameters then add the default | 1258 // no arguments set in the query parameters then add the default |
1137 // NaClTerm.argv arguments. | 1259 // NaClTerm.argv arguments. |
1138 // TODO(bradnelson): Consider dropping this method of passing in parameters. | 1260 // TODO(bradnelson): Consider dropping this method of passing in parameters. |
1139 if (args['arg0'] === undefined && args['arg1'] === undefined && argv) { | 1261 if (args.arg0 === undefined && args.arg1 === undefined && argv) { |
1140 var argn = 0; | 1262 var argn = 0; |
1141 argv.forEach(function(arg) { | 1263 argv.forEach(function (arg) { |
1142 var argname = 'arg' + argn; | 1264 var argname = 'arg' + argn; |
1143 addParam(argname, arg); | 1265 addParam(argname, arg); |
1144 argn = argn + 1; | 1266 argn = argn + 1; |
1145 }); | 1267 }); |
1146 } | 1268 } |
1147 | 1269 |
1148 self.pipeServer.addProcessPipes(fg.pid, params); | 1270 self.pipeServer.addProcessPipes(fg.pid, params); |
1149 | 1271 |
1150 if (params[NaClProcessManager.ENV_SPAWN_MODE] === | 1272 if (params[NaClProcessManager.ENV_SPAWN_MODE] === |
1151 NaClProcessManager.ENV_SPAWN_POPUP_VALUE) { | 1273 NaClProcessManager.ENV_SPAWN_POPUP_VALUE) { |
1152 var popup = new GraphicalPopup( | 1274 var popup = new GraphicalPopup( |
1153 fg, | 1275 fg, |
1154 parseInt(params[NaClProcessManager.ENV_POPUP_WIDTH] || | 1276 parseInt(params[NaClProcessManager.ENV_POPUP_WIDTH] || |
1155 NaClProcessManager.POPUP_WIDTH_DEFAULT), | 1277 NaClProcessManager.POPUP_WIDTH_DEFAULT, 10), |
1156 parseInt(params[NaClProcessManager.ENV_POPUP_HEIGHT] || | 1278 parseInt(params[NaClProcessManager.ENV_POPUP_HEIGHT] || |
1157 NaClProcessManager.POPUP_HEIGHT_DEFAULT), | 1279 NaClProcessManager.POPUP_HEIGHT_DEFAULT, 10), |
1158 argv[0] | 1280 argv[0] |
1159 ); | 1281 ); |
1160 popup.setClosedListener(function() { | 1282 popup.setClosedListener(function () { |
1161 self.exit(NaClProcessManager.EXIT_CODE_KILL, popup.process); | 1283 self.exit(NaClProcessManager.EXIT_CODE_KILL, popup.process); |
1162 GraphicalPopup.focusCurrentWindow(); | 1284 GraphicalPopup.focusCurrentWindow(); |
1163 }); | 1285 }); |
1164 | 1286 |
1165 fg.popup = popup; | 1287 fg.popup = popup; |
1166 | 1288 |
1167 popup.create(); | 1289 popup.create(); |
1168 } else { | 1290 } else { |
1169 if (params[NaClProcessManager.ENV_SPAWN_MODE] === | 1291 if (params[NaClProcessManager.ENV_SPAWN_MODE] === |
1170 NaClProcessManager.ENV_SPAWN_EMBED_VALUE) { | 1292 NaClProcessManager.ENV_SPAWN_EMBED_VALUE) { |
1171 var style = fg.style; | 1293 var style = fg.style; |
1172 style.position = 'absolute'; | 1294 style.position = 'absolute'; |
1173 style.top = 0; | 1295 style.top = 0; |
1174 style.left = 0; | 1296 style.left = 0; |
1175 style.width = params[NaClProcessManager.ENV_EMBED_WIDTH] || | 1297 style.width = params[NaClProcessManager.ENV_EMBED_WIDTH] || |
1176 NaClProcessManager.EMBED_WIDTH_DEFAULT; | 1298 NaClProcessManager.EMBED_WIDTH_DEFAULT; |
1177 style.height = params[NaClProcessManager.ENV_EMBED_HEIGHT] || | 1299 style.height = params[NaClProcessManager.ENV_EMBED_HEIGHT] || |
1178 NaClProcessManager.EMBED_HEIGHT_DEFAULT; | 1300 NaClProcessManager.EMBED_HEIGHT_DEFAULT; |
1179 } | 1301 } |
1180 document.body.appendChild(fg); | 1302 document.body.appendChild(fg); |
1181 } | 1303 } |
1182 | 1304 |
1183 // Work around crbug.com/350445 | 1305 // Work around crbug.com/350445 |
1184 var junk = fg.offsetTop; | 1306 window.junk = fg.offsetTop; |
1185 | 1307 |
1186 // Set a startup timeout to detect the case when running a module | 1308 // Set a startup timeout to detect the case when running a module |
1187 // from html5 storage but nacl is not enabled. | 1309 // from html5 storage but nacl is not enabled. |
1188 fg.moduleResponded = false; | 1310 fg.moduleResponded = false; |
1189 setTimeout(function() { | 1311 setTimeout(function () { |
1190 self.handleStartupTimeout_(fg); | 1312 self.handleStartupTimeout_(fg); |
1191 }, 500); | 1313 }, 500); |
1192 | 1314 |
1193 // yield result. | 1315 // yield result. |
1194 callback(fg.pid, fg); | 1316 callback(fg.pid, fg); |
1195 }); | 1317 }); |
1196 }; | 1318 }; |
1197 | 1319 |
1198 /** | 1320 /** |
1199 * Handles the exiting of a process. | 1321 * Handles the exiting of a process. |
1200 * @callback waitCallback | 1322 * @callback waitCallback |
1201 * @param {number} pid The PID of the process that exited or an error code on | 1323 * @param {number} pid The PID of the process that exited or an error code on |
1202 * error. | 1324 * error. |
1203 * @param {number} status The exit code of the process. | 1325 * @param {number} status The exit code of the process. |
1204 */ | 1326 */ |
1205 | 1327 |
1206 /** | 1328 /** |
1207 * Waits for the process identified by pid to exit, and call a callback when | 1329 * Waits for the process identified by pid to exit, and call a callback when |
1208 * finished. | 1330 * finished. |
1209 * @param {number} pid The process ID of the process. If pid==-1, wait for any | 1331 * @param {number} pid The process ID of the process. If pid==-1, wait for any |
1210 * child. If pid==0, wait for any child in the caller's process group. If | 1332 * child. If pid==0, wait for any child in the caller's process group. If |
1211 * pid<-1, wait for any child in the process group with process group | 1333 * pid<-1, wait for any child in the process group with process group |
1212 * id==|pid|. | 1334 * id==|pid|. |
1213 * @param {number} options The desired options, ORed together. | 1335 * @param {number} options The desired options, ORed together. |
1214 * @param {waitCallback} reply The callback to be called when the process has | 1336 * @param {waitCallback} reply The callback to be called when the process has |
1215 * exited. | 1337 * exited. |
1216 * @param {number} [srcPid=1] The process PID of the calling process. Assume | 1338 * @param {number} [srcPid=1] The process PID of the calling process. Assume |
1217 * the init process if omitted. | 1339 * the init process if omitted. |
1218 */ | 1340 */ |
1219 NaClProcessManager.prototype.waitpid = function(pid, options, reply, srcPid) { | 1341 NaClProcessManager.prototype.waitpid = function (pid, options, reply, srcPid) { |
1220 var self = this; | |
1221 if (srcPid === undefined) { | 1342 if (srcPid === undefined) { |
1222 srcPid = NaClProcessManager.INIT_PID; | 1343 srcPid = NaClProcessManager.INIT_PID; |
1223 } | 1344 } |
1224 | 1345 |
1225 // The specified process does not exist or is not a child. | 1346 // The specified process does not exist or is not a child. |
1226 if (pid > 0 && (this.processes[pid] === undefined || | 1347 if (pid > 0 && (this.processes[pid] === undefined || |
1227 this.processes[pid].ppid !== srcPid)) { | 1348 this.processes[pid].ppid !== srcPid)) { |
1228 reply(-Errno.ECHILD, 0); | 1349 reply(-Errno.ECHILD, 0); |
1229 return; | 1350 return; |
1230 } | 1351 } |
1231 | 1352 |
1232 // The specified process has already finished. | 1353 // The specified process has already finished. |
1233 if (pid > 0 && this.processes[pid].exitCode !== null) { | 1354 if (pid > 0 && this.processes[pid].exitCode !== null) { |
1234 var exitCode = this.processes[pid].exitCode; | 1355 var exitCode = this.processes[pid].exitCode; |
1235 this.deleteProcessEntry_(pid); | 1356 this.deleteProcessEntry(pid); |
1236 reply(pid, exitCode); | 1357 reply(pid, exitCode); |
1237 return; | 1358 return; |
1238 } | 1359 } |
1239 | 1360 |
1240 // There should be no processes in the init process group. | 1361 // There should be no processes in the init process group. |
1241 if (pid === 0 && srcPid === NaClProcessManager.INIT_PID) { | 1362 if (pid === 0 && srcPid === NaClProcessManager.INIT_PID) { |
1242 reply(-Errno.ECHILD, 0); | 1363 reply(-Errno.ECHILD, 0); |
1243 return; | 1364 return; |
1244 } | 1365 } |
1245 | 1366 |
1246 // pid = 0 waits for processes in the calling process's group. | 1367 // pid = 0 waits for processes in the calling process's group. |
1247 if (pid === 0) { | 1368 if (pid === 0) { |
1248 pid = -this.processes[srcPid].pgid; | 1369 pid = -this.processes[srcPid].pgid; |
1249 } | 1370 } |
1250 | 1371 |
1251 if (pid < 0) { | 1372 if (pid < 0) { |
1252 var finishedPid = null; | 1373 var finishedPid = null; |
1253 for (var processPid in this.processes) { | 1374 var that = this; |
1254 var process = this.processes[processPid]; | 1375 Object.keys(this.processes).some(function (processPid) { |
| 1376 var process = that.processes[processPid]; |
1255 if (process.exitCode !== null && | 1377 if (process.exitCode !== null && |
1256 process.ppid === srcPid && | 1378 process.ppid === srcPid && |
1257 (pid === -1 || process.pgid === -pid)) { | 1379 (pid === -1 || process.pgid === -pid)) { |
1258 finishedPid = parseInt(processPid); | 1380 finishedPid = parseInt(processPid, 10); |
1259 break; | 1381 return true; |
1260 } | 1382 } |
1261 } | 1383 return false; |
| 1384 }); |
1262 | 1385 |
1263 if (finishedPid !== null) { | 1386 if (finishedPid !== null) { |
1264 reply(finishedPid, this.processes[finishedPid].exitCode); | 1387 reply(finishedPid, this.processes[finishedPid].exitCode); |
1265 this.deleteProcessEntry_(finishedPid); | 1388 this.deleteProcessEntry(finishedPid); |
1266 return; | 1389 return; |
1267 } | 1390 } |
1268 } | 1391 } |
1269 | 1392 |
1270 if ((options & NaClProcessManager.WNOHANG) !== 0) { | 1393 if ((options & NaClProcessManager.WNOHANG) !== 0) { |
1271 reply(0, 0); | 1394 reply(0, 0); |
1272 return; | 1395 return; |
1273 } | 1396 } |
1274 | 1397 |
1275 // Add the waitpid call to the waiter list. | 1398 // Add the waitpid call to the waiter list. |
1276 if (!this.waiters[pid]) { | 1399 if (!this.waiters[pid]) { |
1277 this.waiters[pid] = []; | 1400 this.waiters[pid] = []; |
1278 } | 1401 } |
1279 this.waiters[pid].push({ | 1402 this.waiters[pid].push({ |
1280 reply: reply, | 1403 reply: reply, |
1281 options: options, | 1404 options: options, |
1282 srcPid: srcPid | 1405 srcPid: srcPid |
1283 }); | 1406 }); |
1284 }; | 1407 }; |
1285 | 1408 |
1286 /** | 1409 /** |
1287 * Update the dimensions of the terminal on terminal resize. | 1410 * Update the dimensions of the terminal on terminal resize. |
1288 * @param {number} width The width of the terminal. | 1411 * @param {number} width The width of the terminal. |
1289 * @param {number} height The height of the terminal. | 1412 * @param {number} height The height of the terminal. |
1290 */ | 1413 */ |
1291 NaClProcessManager.prototype.onTerminalResize = function(width, height) { | 1414 NaClProcessManager.prototype.onTerminalResize = function (width, height) { |
1292 this.ttyWidth = width; | 1415 this.ttyWidth = width; |
1293 this.ttyHeight = height; | 1416 this.ttyHeight = height; |
1294 if (this.foregroundProcess) { | 1417 if (this.foregroundProcess) { |
1295 this.foregroundProcess.postMessage({'tty_resize': [ width, height ]}); | 1418 this.foregroundProcess.postMessage({'tty_resize': [ width, height ]}); |
1296 } | 1419 } |
1297 }; | 1420 }; |
1298 | 1421 |
1299 /** | 1422 /** |
1300 * Handle a SIGINT signal. | 1423 * Handle a SIGINT signal. |
1301 * @returns {boolean} Whether or not the interrupt succeeded. | 1424 * @returns {boolean} Whether or not the interrupt succeeded. |
1302 */ | 1425 */ |
1303 NaClProcessManager.prototype.sigint = function() { | 1426 NaClProcessManager.prototype.sigint = function () { |
1304 if (!this.foregroundProcess) | 1427 if (!this.foregroundProcess) { |
1305 throw new Error(NaClProcessManager.NO_FG_ERROR); | 1428 throw new Error(NaClProcessManager.NO_FG_ERROR); |
| 1429 } |
1306 | 1430 |
1307 // TODO(bradnelson): Change this once we support signals. | 1431 // TODO(bradnelson): Change this once we support signals. |
1308 // Abort on Control+C, but don't quit bash. | 1432 // Abort on Control+C, but don't quit bash. |
1309 if (!this.isRootProcess(this.foregroundProcess)) { | 1433 if (!this.isRootProcess(this.foregroundProcess)) { |
1310 // Only exit if the appropriate environment variable is set. | 1434 // Only exit if the appropriate environment variable is set. |
1311 var query = 'param[name="' + NaClProcessManager.ENV_ABORT + '"]'; | 1435 var query = 'param[name="' + NaClProcessManager.ENV_ABORT + '"]'; |
1312 var enabledEnv = this.foregroundProcess.querySelector(query); | 1436 var enabledEnv = this.foregroundProcess.querySelector(query); |
1313 if (enabledEnv && enabledEnv.value === NaClProcessManager.ENV_ABORT_VALUE) { | 1437 if (enabledEnv && enabledEnv.value === NaClProcessManager.ENV_ABORT_VALUE) { |
1314 this.exit(NaClProcessManager.EXIT_CODE_KILL, this.foregroundProcess); | 1438 this.exit(NaClProcessManager.EXIT_CODE_KILL, this.foregroundProcess); |
1315 return true; | 1439 return true; |
1316 } | 1440 } |
1317 } | 1441 } |
1318 return false; | 1442 return false; |
1319 }; | 1443 }; |
1320 | 1444 |
1321 /** | 1445 /** |
1322 * Send standard input to the foreground process. | 1446 * Send standard input to the foreground process. |
1323 * @param {string} str The string to be sent to as stdin. | 1447 * @param {string} str The string to be sent to as stdin. |
1324 */ | 1448 */ |
1325 NaClProcessManager.prototype.sendStdinForeground = function(str) { | 1449 NaClProcessManager.prototype.sendStdinForeground = function (str) { |
1326 if (!this.foregroundProcess) | 1450 if (!this.foregroundProcess) { |
1327 throw new Error(NaClProcessManager.NO_FG_ERROR); | 1451 throw new Error(NaClProcessManager.NO_FG_ERROR); |
| 1452 } |
1328 | 1453 |
1329 var message = {}; | 1454 var message = {}; |
1330 message[NaClProcessManager.prefix] = str; | 1455 message[NaClProcessManager.prefix] = str; |
1331 this.foregroundProcess.postMessage(message); | 1456 this.foregroundProcess.postMessage(message); |
1332 }; | 1457 }; |
1333 | |
1334 /** | |
1335 * This creates a popup that runs a NaCl process inside. | |
1336 * | |
1337 * @param {Object} process The NaCl process to be run. | |
1338 * @param {number} width | |
1339 * @param {number} height | |
1340 * @param {string} title | |
1341 */ | |
1342 function GraphicalPopup(process, width, height, title) { | |
1343 this.process = process || null; | |
1344 this.width = width || GraphicalPopup.DEFAULT_WIDTH; | |
1345 this.height = height || GraphicalPopup.DEFAULT_HEIGHT; | |
1346 this.title = title || ''; | |
1347 this.win = null; | |
1348 this.onClosed = function () {}; | |
1349 } | |
1350 | |
1351 /** | |
1352 * The default width of the popup. | |
1353 * @type {number} | |
1354 */ | |
1355 GraphicalPopup.DEFAULT_WIDTH = 600; | |
1356 | |
1357 /** | |
1358 * The default height of the popup. | |
1359 * @type {number} | |
1360 */ | |
1361 GraphicalPopup.DEFAULT_HEIGHT = 400; | |
1362 | |
1363 /** | |
1364 * The (empty) HTML file to which the NaCl module is added. | |
1365 * @const | |
1366 */ | |
1367 GraphicalPopup.HTML_FILE = 'graphical.html'; | |
1368 | |
1369 /** | |
1370 * Focus the window in which this code is run. | |
1371 */ | |
1372 GraphicalPopup.focusCurrentWindow = function() { | |
1373 chrome.app.window.current().focus(); | |
1374 }; | |
1375 | |
1376 /** | |
1377 * This callback is called when the popup is closed. | |
1378 * @callback closedCallback | |
1379 */ | |
1380 | |
1381 /** | |
1382 * Set a function to be called as a callback when the popup is closed. | |
1383 * @param {closedCallback} listener | |
1384 */ | |
1385 GraphicalPopup.prototype.setClosedListener = function(listener) { | |
1386 if (this.win) { | |
1387 throw new Error("Cannot set closed listener after creating window."); | |
1388 } | |
1389 this.onClosed = listener; | |
1390 }; | |
1391 | |
1392 /** | |
1393 * Create the window. | |
1394 */ | |
1395 GraphicalPopup.prototype.create = function() { | |
1396 var self = this; | |
1397 chrome.app.window.create('graphical.html', { | |
1398 'bounds': { | |
1399 'width': self.width, | |
1400 'height': self.height | |
1401 }, | |
1402 }, function(win) { | |
1403 var process = self.process; | |
1404 var popup = win.contentWindow; | |
1405 | |
1406 self.win = process.window = win; | |
1407 | |
1408 popup.document.title = self.title; | |
1409 | |
1410 popup.addEventListener('load', function() { | |
1411 process.style.position = 'absolute'; | |
1412 process.style.top = '0'; | |
1413 process.style.left = '0'; | |
1414 process.style.width = '100%'; | |
1415 process.style.height = '100%'; | |
1416 popup.document.body.appendChild(process); | |
1417 }); | |
1418 | |
1419 popup.focused = true; | |
1420 popup.addEventListener('focus', function() { | |
1421 this.focused = true; | |
1422 }); | |
1423 popup.addEventListener('blur', function() { | |
1424 this.focused = false; | |
1425 }); | |
1426 | |
1427 win.onClosed.addListener(self.onClosed); | |
1428 }); | |
1429 }; | |
1430 | |
1431 /** | |
1432 * Close the popup. | |
1433 */ | |
1434 GraphicalPopup.prototype.destroy = function() { | |
1435 if (this.win.contentWindow.focused) { | |
1436 GraphicalPopup.focusCurrentWindow(); | |
1437 } | |
1438 this.win.onClosed.removeListener(this.onClosed); | |
1439 this.win.close(); | |
1440 | |
1441 this.process = null; | |
1442 this.win = null; | |
1443 }; | |
OLD | NEW |