Index: extensions/renderer/resources/serial_service.js |
diff --git a/extensions/renderer/resources/serial_service.js b/extensions/renderer/resources/serial_service.js |
index 92e17d06ec2b8dfb8936021ec29f1fbf6ddf7abb..00d137b1ea1cac14226803db8fd829650b2c83c7 100644 |
--- a/extensions/renderer/resources/serial_service.js |
+++ b/extensions/renderer/resources/serial_service.js |
@@ -5,21 +5,20 @@ |
define('serial_service', [ |
'content/public/renderer/service_provider', |
'device/serial/serial.mojom', |
+ 'mojo/public/js/bindings/core', |
'mojo/public/js/bindings/router', |
-], function(serviceProvider, serialMojom, routerModule) { |
+], function(serviceProvider, serialMojom, core, routerModule) { |
+ /** |
+ * A Javascript client for the serial service and connection Mojo services. |
+ * |
+ * This provides a thick client around the Mojo services, exposing a JS-style |
+ * interface to serial connections and information about serial devices. This |
+ * converts parameters and result between the Apps serial API types and the |
+ * Mojo types. |
+ */ |
- function defineService(proxy, handle) { |
- if (!handle) |
- handle = serviceProvider.connectToService(proxy.NAME_); |
- var router = new routerModule.Router(handle); |
- var service = new proxy(router); |
- return { |
- service: service, |
- router: router, |
- }; |
- } |
- |
- var service = defineService(serialMojom.SerialServiceProxy).service; |
+ var service = new serialMojom.SerialServiceProxy(new routerModule.Router( |
+ serviceProvider.connectToService(serialMojom.SerialServiceProxy.NAME_))); |
function getDevices() { |
return service.getDevices().then(function(response) { |
@@ -36,7 +35,240 @@ define('serial_service', [ |
}); |
} |
+ var DEFAULT_CLIENT_OPTIONS = { |
+ persistent: false, |
+ name: '', |
+ receiveTimeout: 0, |
+ sendTimeout: 0, |
+ bufferSize: 4096, |
+ }; |
+ |
+ var DATA_BITS_TO_MOJO = { |
+ undefined: serialMojom.DataBits.NONE, |
+ 'seven': serialMojom.DataBits.SEVEN, |
+ 'eight': serialMojom.DataBits.EIGHT, |
+ }; |
+ var STOP_BITS_TO_MOJO = { |
+ undefined: serialMojom.StopBits.NONE, |
+ 'one': serialMojom.StopBits.ONE, |
+ 'two': serialMojom.StopBits.TWO, |
+ }; |
+ var PARITY_BIT_TO_MOJO = { |
+ undefined: serialMojom.ParityBit.NONE, |
+ 'no': serialMojom.ParityBit.NO, |
+ 'odd': serialMojom.ParityBit.ODD, |
+ 'even': serialMojom.ParityBit.EVEN, |
+ }; |
+ |
+ function invertMap(input) { |
+ var output = {}; |
+ for (var key in input) { |
+ if (key == 'undefined') |
+ output[input[key]] = undefined; |
+ else |
+ output[input[key]] = key; |
+ } |
+ return output; |
+ } |
+ 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); |
+ |
+ function getServiceOptions(options) { |
+ var out = {}; |
+ if (options.dataBits) |
+ out.data_bits = DATA_BITS_TO_MOJO[options.dataBits]; |
+ if (options.stopBits) |
+ out.stop_bits = STOP_BITS_TO_MOJO[options.stopBits]; |
+ if (options.parityBit) |
+ out.parity_bit = PARITY_BIT_TO_MOJO[options.parityBit]; |
+ if ('ctsFlowControl' in options) { |
+ out.has_cts_flow_control = true; |
+ out.cts_flow_control = options.ctsFlowControl; |
+ } |
+ if ('bitrate' in options) |
+ out.bitrate = options.bitrate; |
+ return out; |
+ } |
+ |
+ function convertServiceInfo(result) { |
+ if (!result.info) |
+ throw new Error('Failed to get ConnectionInfo.'); |
+ return { |
+ ctsFlowControl: !!result.info.cts_flow_control, |
+ bitrate: result.info.bitrate || undefined, |
+ dataBits: DATA_BITS_FROM_MOJO[result.info.data_bits], |
+ stopBits: STOP_BITS_FROM_MOJO[result.info.stop_bits], |
+ parityBit: PARITY_BIT_FROM_MOJO[result.info.parity_bit], |
+ }; |
+ } |
+ |
+ function Connection(remoteConnection, router, 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); |
+ } |
+ |
+ Connection.create = function(path, options) { |
+ options = options || {}; |
+ var serviceOptions = getServiceOptions(options); |
+ var pipe = core.createMessagePipe(); |
+ service.connect(path, serviceOptions, pipe.handle0); |
+ var router = new routerModule.Router(pipe.handle1); |
+ var connection = new serialMojom.ConnectionProxy(router); |
+ return connection.getInfo().then(convertServiceInfo).then( |
+ function(info) { |
+ return Promise.all([info, allocateConnectionId()]); |
+ }).catch(function(e) { |
+ router.close(); |
+ throw e; |
+ }).then(function(results) { |
+ var info = results[0]; |
+ var id = results[1]; |
+ var serialConnectionClient = new Connection( |
+ connection, router, id, options); |
+ var clientInfo = serialConnectionClient.getClientInfo_(); |
+ for (var key in clientInfo) { |
+ info[key] = clientInfo[key]; |
+ } |
+ return { |
+ connection: serialConnectionClient, |
+ info: info, |
+ }; |
+ }); |
+ }; |
+ |
+ Connection.prototype.close = function() { |
+ this.router_.close(); |
+ return getConnections().then(function(connections) { |
+ delete connections[this.id_] |
+ return true; |
+ }.bind(this)); |
+ }; |
+ |
+ Connection.prototype.getClientInfo_ = function() { |
+ var info = { |
+ connectionId: this.id_, |
+ paused: this.paused_, |
+ } |
+ for (var key in this.options_) { |
+ info[key] = this.options_[key]; |
+ } |
+ return info; |
+ }; |
+ |
+ Connection.prototype.getInfo = function() { |
+ var info = this.getClientInfo_(); |
+ return this.remoteConnection_.getInfo().then(convertServiceInfo).then( |
+ function(result) { |
+ for (var key in result) { |
+ info[key] = result[key]; |
+ } |
+ return info; |
+ }).catch(function() { |
+ return info; |
+ }); |
+ }; |
+ |
+ Connection.prototype.setClientOptions_ = function(options) { |
+ if ('name' in options) |
+ this.options_.name = options.name; |
+ if ('receiveTimeout' in options) |
+ this.options_.receiveTimeout = options.receiveTimeout; |
+ if ('sendTimeout' in options) |
+ this.options_.sendTimeout = options.sendTimeout; |
+ if ('bufferSize' in options) |
+ this.options_.bufferSize = options.bufferSize; |
+ }; |
+ |
+ Connection.prototype.setOptions = function(options) { |
+ this.setClientOptions_(options); |
+ var serviceOptions = getServiceOptions(options); |
+ if ($Object.keys(serviceOptions).length == 0) |
+ return true; |
+ return this.remoteConnection_.setOptions(serviceOptions).then( |
+ function(result) { |
+ return !!result.success; |
+ }).catch(function() { |
+ return false; |
+ }); |
+ }; |
+ |
+ Connection.prototype.getControlSignals = function() { |
+ return this.remoteConnection_.getControlSignals().then(function(result) { |
+ if (!result.signals) |
+ throw new Error('Failed to get control signals.'); |
+ var signals = result.signals; |
+ return { |
+ dcd: !!signals.dcd, |
+ cts: !!signals.cts, |
+ ri: !!signals.ri, |
+ dsr: !!signals.dsr, |
+ }; |
+ }); |
+ }; |
+ |
+ Connection.prototype.setControlSignals = function(signals) { |
+ var controlSignals = {}; |
+ if ('dtr' in signals) { |
+ controlSignals.has_dtr = true; |
+ controlSignals.dtr = signals.dtr; |
+ } |
+ if ('rts' in signals) { |
+ controlSignals.has_rts = true; |
+ controlSignals.rts = signals.rts; |
+ } |
+ return this.remoteConnection_.setControlSignals(controlSignals).then( |
+ function(result) { |
+ return !!result.success; |
+ }); |
+ }; |
+ |
+ Connection.prototype.flush = function() { |
+ return this.remoteConnection_.flush().then(function(result) { |
+ return !!result.success; |
+ }); |
+ }; |
+ |
+ Connection.prototype.setPaused = function(paused) { |
+ this.paused_ = paused; |
+ }; |
+ |
+ var connections_ = {}; |
+ var nextConnectionId_ = 0; |
+ |
+ // Wrap all access to |connections_| through getConnections to avoid adding |
+ // any synchronous dependencies on it. This will likely be important when |
+ // supporting persistent connections by stashing them. |
+ function getConnections() { |
+ return Promise.resolve(connections_); |
+ } |
+ |
+ function getConnection(id) { |
+ return getConnections().then(function(connections) { |
+ if (!connections[id]) |
+ throw new Error ('Serial connection not found.'); |
+ return connections[id]; |
+ }); |
+ } |
+ |
+ function allocateConnectionId() { |
+ return Promise.resolve(nextConnectionId_++); |
+ } |
+ |
return { |
getDevices: getDevices, |
+ createConnection: Connection.create, |
+ getConnection: getConnection, |
+ getConnections: getConnections, |
}; |
}); |