Chromium Code Reviews| Index: extensions/renderer/resources/serial_service.js |
| diff --git a/extensions/renderer/resources/serial_service.js b/extensions/renderer/resources/serial_service.js |
| index b8b0970db71ab70e1921064d4570c6ed204a6ae6..a47ec191d8cfda59c618b45aca4a9553c4fd60c8 100644 |
| --- a/extensions/renderer/resources/serial_service.js |
| +++ b/extensions/renderer/resources/serial_service.js |
| @@ -4,10 +4,17 @@ |
| define('serial_service', [ |
| 'content/public/renderer/service_provider', |
| + 'data_receiver', |
| + 'data_sender', |
| 'device/serial/serial.mojom', |
| 'mojo/public/js/bindings/core', |
| 'mojo/public/js/bindings/router', |
| -], function(serviceProvider, serialMojom, core, routerModule) { |
| +], function(serviceProvider, |
| + dataReceiver, |
| + dataSender, |
| + serialMojom, |
| + core, |
| + routerModule) { |
| /** |
| * A Javascript client for the serial service and connection Mojo services. |
| * |
| @@ -59,6 +66,20 @@ define('serial_service', [ |
| 'odd': serialMojom.ParityBit.ODD, |
| 'even': serialMojom.ParityBit.EVEN, |
| }; |
| + var SEND_ERROR_TO_MOJO = { |
| + undefined: serialMojom.SendError.NONE, |
| + 'disconnected': serialMojom.SendError.DISCONNECTED, |
| + 'pending': serialMojom.SendError.PENDING, |
| + 'timeout': serialMojom.SendError.TIMEOUT, |
| + 'system_error': serialMojom.SendError.SYSTEM_ERROR, |
| + }; |
| + var RECEIVE_ERROR_TO_MOJO = { |
| + undefined: serialMojom.ReceiveError.NONE, |
| + 'disconnected': serialMojom.ReceiveError.DISCONNECTED, |
| + 'device_lost': serialMojom.ReceiveError.DEVICE_LOST, |
| + 'timeout': serialMojom.ReceiveError.TIMEOUT, |
| + 'system_error': serialMojom.ReceiveError.SYSTEM_ERROR, |
| + }; |
| function invertMap(input) { |
| var output = {}; |
| @@ -73,6 +94,8 @@ define('serial_service', [ |
| var DATA_BITS_FROM_MOJO = invertMap(DATA_BITS_TO_MOJO); |
| var STOP_BITS_FROM_MOJO = invertMap(STOP_BITS_TO_MOJO); |
| var PARITY_BIT_FROM_MOJO = invertMap(PARITY_BIT_TO_MOJO); |
| + var SEND_ERROR_FROM_MOJO = invertMap(SEND_ERROR_TO_MOJO); |
| + var RECEIVE_ERROR_FROM_MOJO = invertMap(RECEIVE_ERROR_TO_MOJO); |
| function getServiceOptions(options) { |
| var out = {}; |
| @@ -103,29 +126,36 @@ define('serial_service', [ |
| }; |
| } |
| - function Connection(remoteConnection, router, id, options) { |
| + function Connection( |
| + remoteConnection, router, receivePipe, sendPipe, id, options) { |
| this.remoteConnection_ = remoteConnection; |
| this.router_ = router; |
| - this.id_ = id; |
| - getConnections().then(function(connections) { |
| - connections[this.id_] = this; |
| - }.bind(this)); |
| - this.paused_ = false; |
| this.options_ = {}; |
| for (var key in DEFAULT_CLIENT_OPTIONS) { |
| this.options_[key] = DEFAULT_CLIENT_OPTIONS[key]; |
| } |
| this.setClientOptions_(options); |
| + this.receivePipe_ = |
| + new dataReceiver.DataReceiver(receivePipe, |
| + this.options_.bufferSize, |
| + serialMojom.ReceiveError.DISCONNECTED); |
| + this.sendPipe_ = new dataSender.DataSender( |
| + sendPipe, this.options_.bufferSize, serialMojom.SendError.DISCONNECTED); |
| + this.id_ = id; |
| + getConnections().then(function(connections) { |
| + connections[this.id_] = this; |
| + }.bind(this)); |
| + this.paused_ = false; |
| + this.sendInProgress_ = false; |
| + this.pendingReceive_ = null; |
| + this.pendingError_ = null; |
| + this.startReceive_(); |
| } |
| Connection.create = function(path, options) { |
| options = options || {}; |
| var serviceOptions = getServiceOptions(options); |
| var pipe = core.createMessagePipe(); |
| - // Note: These two are created and closed because the service implementation |
| - // requires that we provide valid message pipes for the data source and |
| - // sink. Currently the client handles are immediately closed; the real |
| - // implementation will come later. |
| var sendPipe = core.createMessagePipe(); |
| var receivePipe = core.createMessagePipe(); |
| service.connect(path, |
| @@ -133,21 +163,24 @@ define('serial_service', [ |
| pipe.handle0, |
| sendPipe.handle0, |
| receivePipe.handle0); |
| - core.close(sendPipe.handle1); |
| - core.close(receivePipe.handle1); |
| var router = new routerModule.Router(pipe.handle1); |
| var connection = new serialMojom.ConnectionProxy(router); |
| - return connection.getInfo().then(convertServiceInfo).then( |
| - function(info) { |
| + return connection.getInfo().then(convertServiceInfo).then(function(info) { |
| return Promise.all([info, allocateConnectionId()]); |
| }).catch(function(e) { |
| router.close(); |
| + core.close(sendPipe.handle1); |
| + core.close(receivePipe.handle1); |
| throw e; |
| }).then(function(results) { |
| var info = results[0]; |
| var id = results[1]; |
| - var serialConnectionClient = new Connection( |
| - connection, router, id, options); |
| + var serialConnectionClient = new Connection(connection, |
| + router, |
| + receivePipe.handle1, |
| + sendPipe.handle1, |
| + id, |
| + options); |
| var clientInfo = serialConnectionClient.getClientInfo_(); |
| for (var key in clientInfo) { |
| info[key] = clientInfo[key]; |
| @@ -161,8 +194,12 @@ define('serial_service', [ |
| Connection.prototype.close = function() { |
| this.router_.close(); |
| + this.receivePipe_.close(); |
| + this.sendPipe_.close(); |
| + clearTimeout(this.receiveTimeoutId_); |
| + clearTimeout(this.sendTimeoutId_); |
| return getConnections().then(function(connections) { |
| - delete connections[this.id_] |
| + delete connections[this.id_]; |
| return true; |
| }.bind(this)); |
| }; |
| @@ -171,7 +208,7 @@ define('serial_service', [ |
| var info = { |
| connectionId: this.id_, |
| paused: this.paused_, |
| - } |
| + }; |
| for (var key in this.options_) { |
| info[key] = this.options_[key]; |
| } |
| @@ -253,6 +290,98 @@ define('serial_service', [ |
| Connection.prototype.setPaused = function(paused) { |
| this.paused_ = paused; |
| + if (paused) { |
| + clearTimeout(this.receiveTimeoutId_); |
| + this.receiveTimeoutId_ = null; |
| + return; |
| + } |
|
raymes
2014/08/29 06:07:16
Using if/else here probably makes this slightly cl
Sam McNally
2014/09/01 06:35:17
Done.
|
| + if (!this.receiveInProgress_) |
| + this.startReceive_(); |
| + }; |
| + |
| + Connection.prototype.send = function(data) { |
| + if (this.sendInProgress_) |
| + return Promise.resolve({bytesSent: 0, error: 'pending'}); |
| + |
| + if (this.options_.sendTimeout) { |
| + this.sendTimeoutId_ = setTimeout(function() { |
| + this.sendPipe_.cancel(serialMojom.SendError.TIMEOUT); |
| + }.bind(this), this.options_.sendTimeout); |
| + } |
| + this.sendInProgress_ = true; |
| + return this.sendPipe_.send(data).then(function(bytesSent) { |
| + return {bytesSent: bytesSent}; |
| + }).catch(function(e) { |
| + return { |
| + bytesSent: e.bytesSent || 0, |
| + error: SEND_ERROR_FROM_MOJO[e.error] || 'system_error', |
|
raymes
2014/08/29 06:07:15
In what case will it return 'system_error'?
Sam McNally
2014/09/01 06:35:18
It shouldn't hit that case.
|
| + }; |
| + }).then(function(result) { |
| + if (this.sendTimeoutId_) |
| + clearTimeout(this.sendTimeoutId_); |
| + this.sendTimeoutId_ = null; |
| + this.sendInProgress_ = false; |
| + return result; |
| + }.bind(this)); |
| + }; |
| + |
| + Connection.prototype.startReceive_ = function() { |
| + this.receiveInProgress_ = true; |
| + var receivePromise = null; |
| + if (this.pendingReceive_) { |
|
raymes
2014/08/29 06:07:15
Some comments describing how pendingReceive_/pendi
Sam McNally
2014/09/01 06:35:17
Done.
|
| + receivePromise = Promise.resolve(this.pendingReceive_); |
| + this.pendingReceive_ = null; |
| + } else if (this.pendingError_) { |
| + receivePromise = Promise.reject(this.pendingError_); |
| + this.pendingError_ = null; |
| + } else { |
| + receivePromise = this.receivePipe_.receive(); |
| + } |
| + receivePromise.then(this.onDataReceived_.bind(this)).catch( |
| + this.onReceiveError_.bind(this)); |
| + this.startReceiveTimeoutTimer_(); |
| + }; |
| + |
| + Connection.prototype.onDataReceived_ = function(data) { |
| + this.startReceiveTimeoutTimer_(); |
| + this.receiveInProgress_ = false; |
| + if (this.paused_) { |
| + this.pendingReceive_ = data; |
| + return; |
| + } |
| + if (this.onData) { |
| + this.onData(data); |
| + } |
| + if (!this.paused_) { |
| + this.startReceive_(); |
| + } |
| + }; |
| + |
| + Connection.prototype.onReceiveError_ = function(e) { |
| + clearTimeout(this.receiveTimeoutId_); |
| + this.receiveInProgress_ = false; |
| + if (this.paused_) { |
| + this.pendingError_ = e; |
| + return; |
| + } |
| + var error = e.error || serialMojom.ReceiveError.SYSTEM_ERROR; |
| + this.paused_ = true; |
| + if (this.onError) |
| + this.onError(RECEIVE_ERROR_FROM_MOJO[error]); |
| + }; |
| + |
| + Connection.prototype.startReceiveTimeoutTimer_ = function() { |
| + clearTimeout(this.receiveTimeoutId_); |
| + if (this.options_.receiveTimeout && !this.paused_) { |
| + this.receiveTimeoutId_ = setTimeout(this.onReceiveTimeout_.bind(this), |
| + this.options_.receiveTimeout); |
| + } |
| + }; |
| + |
| + Connection.prototype.onReceiveTimeout_ = function() { |
| + if (this.onError) |
| + this.onError('timeout'); |
| + this.startReceiveTimeoutTimer_(); |
| }; |
| var connections_ = {}; |
| @@ -268,7 +397,7 @@ define('serial_service', [ |
| function getConnection(id) { |
| return getConnections().then(function(connections) { |
| if (!connections[id]) |
| - throw new Error ('Serial connection not found.'); |
| + throw new Error('Serial connection not found.'); |
| return connections[id]; |
| }); |
| } |
| @@ -282,5 +411,7 @@ define('serial_service', [ |
| createConnection: Connection.create, |
| getConnection: getConnection, |
| getConnections: getConnections, |
| + // For testing. |
| + Connection: Connection, |
| }; |
| }); |