Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(207)

Side by Side Diff: extensions/renderer/resources/serial_service.js

Issue 509813002: Implement the client side of Serial I/O on data pipe. (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@serial-io
Patch Set: remove TimeoutManager.pause() Created 6 years, 3 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
1 // Copyright 2014 The Chromium Authors. All rights reserved. 1 // Copyright 2014 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be 2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file. 3 // found in the LICENSE file.
4 4
5 define('serial_service', [ 5 define('serial_service', [
6 'content/public/renderer/service_provider', 6 'content/public/renderer/service_provider',
7 'data_receiver',
8 'data_sender',
7 'device/serial/serial.mojom', 9 'device/serial/serial.mojom',
8 'mojo/public/js/bindings/core', 10 'mojo/public/js/bindings/core',
9 'mojo/public/js/bindings/router', 11 'mojo/public/js/bindings/router',
10 ], function(serviceProvider, serialMojom, core, routerModule) { 12 ], function(serviceProvider,
13 dataReceiver,
14 dataSender,
15 serialMojom,
16 core,
17 routerModule) {
11 /** 18 /**
12 * A Javascript client for the serial service and connection Mojo services. 19 * A Javascript client for the serial service and connection Mojo services.
13 * 20 *
14 * This provides a thick client around the Mojo services, exposing a JS-style 21 * This provides a thick client around the Mojo services, exposing a JS-style
15 * interface to serial connections and information about serial devices. This 22 * interface to serial connections and information about serial devices. This
16 * converts parameters and result between the Apps serial API types and the 23 * converts parameters and result between the Apps serial API types and the
17 * Mojo types. 24 * Mojo types.
18 */ 25 */
19 26
20 var service = new serialMojom.SerialServiceProxy(new routerModule.Router( 27 var service = new serialMojom.SerialServiceProxy(new routerModule.Router(
(...skipping 31 matching lines...) Expand 10 before | Expand all | Expand 10 after
52 undefined: serialMojom.StopBits.NONE, 59 undefined: serialMojom.StopBits.NONE,
53 'one': serialMojom.StopBits.ONE, 60 'one': serialMojom.StopBits.ONE,
54 'two': serialMojom.StopBits.TWO, 61 'two': serialMojom.StopBits.TWO,
55 }; 62 };
56 var PARITY_BIT_TO_MOJO = { 63 var PARITY_BIT_TO_MOJO = {
57 undefined: serialMojom.ParityBit.NONE, 64 undefined: serialMojom.ParityBit.NONE,
58 'no': serialMojom.ParityBit.NO, 65 'no': serialMojom.ParityBit.NO,
59 'odd': serialMojom.ParityBit.ODD, 66 'odd': serialMojom.ParityBit.ODD,
60 'even': serialMojom.ParityBit.EVEN, 67 'even': serialMojom.ParityBit.EVEN,
61 }; 68 };
69 var SEND_ERROR_TO_MOJO = {
70 undefined: serialMojom.SendError.NONE,
71 'disconnected': serialMojom.SendError.DISCONNECTED,
72 'pending': serialMojom.SendError.PENDING,
73 'timeout': serialMojom.SendError.TIMEOUT,
74 'system_error': serialMojom.SendError.SYSTEM_ERROR,
75 };
76 var RECEIVE_ERROR_TO_MOJO = {
77 undefined: serialMojom.ReceiveError.NONE,
78 'disconnected': serialMojom.ReceiveError.DISCONNECTED,
79 'device_lost': serialMojom.ReceiveError.DEVICE_LOST,
80 'timeout': serialMojom.ReceiveError.TIMEOUT,
81 'system_error': serialMojom.ReceiveError.SYSTEM_ERROR,
82 };
62 83
63 function invertMap(input) { 84 function invertMap(input) {
64 var output = {}; 85 var output = {};
65 for (var key in input) { 86 for (var key in input) {
66 if (key == 'undefined') 87 if (key == 'undefined')
67 output[input[key]] = undefined; 88 output[input[key]] = undefined;
68 else 89 else
69 output[input[key]] = key; 90 output[input[key]] = key;
70 } 91 }
71 return output; 92 return output;
72 } 93 }
73 var DATA_BITS_FROM_MOJO = invertMap(DATA_BITS_TO_MOJO); 94 var DATA_BITS_FROM_MOJO = invertMap(DATA_BITS_TO_MOJO);
74 var STOP_BITS_FROM_MOJO = invertMap(STOP_BITS_TO_MOJO); 95 var STOP_BITS_FROM_MOJO = invertMap(STOP_BITS_TO_MOJO);
75 var PARITY_BIT_FROM_MOJO = invertMap(PARITY_BIT_TO_MOJO); 96 var PARITY_BIT_FROM_MOJO = invertMap(PARITY_BIT_TO_MOJO);
97 var SEND_ERROR_FROM_MOJO = invertMap(SEND_ERROR_TO_MOJO);
98 var RECEIVE_ERROR_FROM_MOJO = invertMap(RECEIVE_ERROR_TO_MOJO);
76 99
77 function getServiceOptions(options) { 100 function getServiceOptions(options) {
78 var out = {}; 101 var out = {};
79 if (options.dataBits) 102 if (options.dataBits)
80 out.data_bits = DATA_BITS_TO_MOJO[options.dataBits]; 103 out.data_bits = DATA_BITS_TO_MOJO[options.dataBits];
81 if (options.stopBits) 104 if (options.stopBits)
82 out.stop_bits = STOP_BITS_TO_MOJO[options.stopBits]; 105 out.stop_bits = STOP_BITS_TO_MOJO[options.stopBits];
83 if (options.parityBit) 106 if (options.parityBit)
84 out.parity_bit = PARITY_BIT_TO_MOJO[options.parityBit]; 107 out.parity_bit = PARITY_BIT_TO_MOJO[options.parityBit];
85 if ('ctsFlowControl' in options) { 108 if ('ctsFlowControl' in options) {
(...skipping 10 matching lines...) Expand all
96 throw new Error('Failed to get ConnectionInfo.'); 119 throw new Error('Failed to get ConnectionInfo.');
97 return { 120 return {
98 ctsFlowControl: !!result.info.cts_flow_control, 121 ctsFlowControl: !!result.info.cts_flow_control,
99 bitrate: result.info.bitrate || undefined, 122 bitrate: result.info.bitrate || undefined,
100 dataBits: DATA_BITS_FROM_MOJO[result.info.data_bits], 123 dataBits: DATA_BITS_FROM_MOJO[result.info.data_bits],
101 stopBits: STOP_BITS_FROM_MOJO[result.info.stop_bits], 124 stopBits: STOP_BITS_FROM_MOJO[result.info.stop_bits],
102 parityBit: PARITY_BIT_FROM_MOJO[result.info.parity_bit], 125 parityBit: PARITY_BIT_FROM_MOJO[result.info.parity_bit],
103 }; 126 };
104 } 127 }
105 128
106 function Connection(remoteConnection, router, id, options) { 129 function Connection(
130 remoteConnection, router, receivePipe, sendPipe, id, options) {
107 this.remoteConnection_ = remoteConnection; 131 this.remoteConnection_ = remoteConnection;
108 this.router_ = router; 132 this.router_ = router;
133 this.options_ = {};
134 for (var key in DEFAULT_CLIENT_OPTIONS) {
135 this.options_[key] = DEFAULT_CLIENT_OPTIONS[key];
136 }
137 this.setClientOptions_(options);
138 this.receivePipe_ =
139 new dataReceiver.DataReceiver(receivePipe,
140 this.options_.bufferSize,
141 serialMojom.ReceiveError.DISCONNECTED);
142 this.sendPipe_ = new dataSender.DataSender(
143 sendPipe, this.options_.bufferSize, serialMojom.SendError.DISCONNECTED);
109 this.id_ = id; 144 this.id_ = id;
110 getConnections().then(function(connections) { 145 getConnections().then(function(connections) {
111 connections[this.id_] = this; 146 connections[this.id_] = this;
112 }.bind(this)); 147 }.bind(this));
113 this.paused_ = false; 148 this.paused_ = false;
114 this.options_ = {}; 149 this.sendInProgress_ = false;
115 for (var key in DEFAULT_CLIENT_OPTIONS) { 150
116 this.options_[key] = DEFAULT_CLIENT_OPTIONS[key]; 151 // queuedReceiveData_ or queuedReceiveError will store the receive result or
117 } 152 // error, respectively, if a receive completes or fails while this
118 this.setClientOptions_(options); 153 // connection is paused. At most one of the the two may be non-null: a
154 // receive completed while paused will only set one of them, no further
155 // receives will be performed while paused and a queued result is dispatched
156 // before any further receives are initiated when unpausing.
157 this.queuedReceiveData_ = null;
158 this.queuedReceiveError = null;
159
160 this.startReceive_();
119 } 161 }
120 162
121 Connection.create = function(path, options) { 163 Connection.create = function(path, options) {
122 options = options || {}; 164 options = options || {};
123 var serviceOptions = getServiceOptions(options); 165 var serviceOptions = getServiceOptions(options);
124 var pipe = core.createMessagePipe(); 166 var pipe = core.createMessagePipe();
125 // Note: These two are created and closed because the service implementation
126 // requires that we provide valid message pipes for the data source and
127 // sink. Currently the client handles are immediately closed; the real
128 // implementation will come later.
129 var sendPipe = core.createMessagePipe(); 167 var sendPipe = core.createMessagePipe();
130 var receivePipe = core.createMessagePipe(); 168 var receivePipe = core.createMessagePipe();
131 service.connect(path, 169 service.connect(path,
132 serviceOptions, 170 serviceOptions,
133 pipe.handle0, 171 pipe.handle0,
134 sendPipe.handle0, 172 sendPipe.handle0,
135 receivePipe.handle0); 173 receivePipe.handle0);
136 core.close(sendPipe.handle1);
137 core.close(receivePipe.handle1);
138 var router = new routerModule.Router(pipe.handle1); 174 var router = new routerModule.Router(pipe.handle1);
139 var connection = new serialMojom.ConnectionProxy(router); 175 var connection = new serialMojom.ConnectionProxy(router);
140 return connection.getInfo().then(convertServiceInfo).then( 176 return connection.getInfo().then(convertServiceInfo).then(function(info) {
141 function(info) {
142 return Promise.all([info, allocateConnectionId()]); 177 return Promise.all([info, allocateConnectionId()]);
143 }).catch(function(e) { 178 }).catch(function(e) {
144 router.close(); 179 router.close();
180 core.close(sendPipe.handle1);
181 core.close(receivePipe.handle1);
145 throw e; 182 throw e;
146 }).then(function(results) { 183 }).then(function(results) {
147 var info = results[0]; 184 var info = results[0];
148 var id = results[1]; 185 var id = results[1];
149 var serialConnectionClient = new Connection( 186 var serialConnectionClient = new Connection(connection,
150 connection, router, id, options); 187 router,
188 receivePipe.handle1,
189 sendPipe.handle1,
190 id,
191 options);
151 var clientInfo = serialConnectionClient.getClientInfo_(); 192 var clientInfo = serialConnectionClient.getClientInfo_();
152 for (var key in clientInfo) { 193 for (var key in clientInfo) {
153 info[key] = clientInfo[key]; 194 info[key] = clientInfo[key];
154 } 195 }
155 return { 196 return {
156 connection: serialConnectionClient, 197 connection: serialConnectionClient,
157 info: info, 198 info: info,
158 }; 199 };
159 }); 200 });
160 }; 201 };
161 202
162 Connection.prototype.close = function() { 203 Connection.prototype.close = function() {
163 this.router_.close(); 204 this.router_.close();
205 this.receivePipe_.close();
206 this.sendPipe_.close();
207 clearTimeout(this.receiveTimeoutId_);
208 clearTimeout(this.sendTimeoutId_);
164 return getConnections().then(function(connections) { 209 return getConnections().then(function(connections) {
165 delete connections[this.id_] 210 delete connections[this.id_];
166 return true; 211 return true;
167 }.bind(this)); 212 }.bind(this));
168 }; 213 };
169 214
170 Connection.prototype.getClientInfo_ = function() { 215 Connection.prototype.getClientInfo_ = function() {
171 var info = { 216 var info = {
172 connectionId: this.id_, 217 connectionId: this.id_,
173 paused: this.paused_, 218 paused: this.paused_,
174 } 219 };
175 for (var key in this.options_) { 220 for (var key in this.options_) {
176 info[key] = this.options_[key]; 221 info[key] = this.options_[key];
177 } 222 }
178 return info; 223 return info;
179 }; 224 };
180 225
181 Connection.prototype.getInfo = function() { 226 Connection.prototype.getInfo = function() {
182 var info = this.getClientInfo_(); 227 var info = this.getClientInfo_();
183 return this.remoteConnection_.getInfo().then(convertServiceInfo).then( 228 return this.remoteConnection_.getInfo().then(convertServiceInfo).then(
184 function(result) { 229 function(result) {
(...skipping 61 matching lines...) Expand 10 before | Expand all | Expand 10 after
246 }; 291 };
247 292
248 Connection.prototype.flush = function() { 293 Connection.prototype.flush = function() {
249 return this.remoteConnection_.flush().then(function(result) { 294 return this.remoteConnection_.flush().then(function(result) {
250 return !!result.success; 295 return !!result.success;
251 }); 296 });
252 }; 297 };
253 298
254 Connection.prototype.setPaused = function(paused) { 299 Connection.prototype.setPaused = function(paused) {
255 this.paused_ = paused; 300 this.paused_ = paused;
301 if (paused) {
302 clearTimeout(this.receiveTimeoutId_);
303 this.receiveTimeoutId_ = null;
304 } else if (!this.receiveInProgress_) {
305 this.startReceive_();
306 }
307 };
308
309 Connection.prototype.send = function(data) {
310 if (this.sendInProgress_)
311 return Promise.resolve({bytesSent: 0, error: 'pending'});
312
313 if (this.options_.sendTimeout) {
314 this.sendTimeoutId_ = setTimeout(function() {
315 this.sendPipe_.cancel(serialMojom.SendError.TIMEOUT);
316 }.bind(this), this.options_.sendTimeout);
317 }
318 this.sendInProgress_ = true;
319 return this.sendPipe_.send(data).then(function(bytesSent) {
320 return {bytesSent: bytesSent};
321 }).catch(function(e) {
322 return {
323 bytesSent: e.bytesSent,
324 error: SEND_ERROR_FROM_MOJO[e.error],
325 };
326 }).then(function(result) {
327 if (this.sendTimeoutId_)
328 clearTimeout(this.sendTimeoutId_);
329 this.sendTimeoutId_ = null;
330 this.sendInProgress_ = false;
331 return result;
332 }.bind(this));
333 };
334
335 Connection.prototype.startReceive_ = function() {
336 this.receiveInProgress_ = true;
337 var receivePromise = null;
338 // If we have a queued receive result, dispatch it immediately instead of
339 // starting a new receive.
340 if (this.queuedReceiveData_) {
341 receivePromise = Promise.resolve(this.queuedReceiveData_);
342 this.queuedReceiveData_ = null;
343 } else if (this.queuedReceiveError) {
344 receivePromise = Promise.reject(this.queuedReceiveError);
345 this.queuedReceiveError = null;
346 } else {
347 receivePromise = this.receivePipe_.receive();
348 }
349 receivePromise.then(this.onDataReceived_.bind(this)).catch(
350 this.onReceiveError_.bind(this));
351 this.startReceiveTimeoutTimer_();
352 };
353
354 Connection.prototype.onDataReceived_ = function(data) {
355 this.startReceiveTimeoutTimer_();
356 this.receiveInProgress_ = false;
357 if (this.paused_) {
358 this.queuedReceiveData_ = data;
359 return;
360 }
361 if (this.onData) {
362 this.onData(data);
363 }
364 if (!this.paused_) {
365 this.startReceive_();
366 }
367 };
368
369 Connection.prototype.onReceiveError_ = function(e) {
370 clearTimeout(this.receiveTimeoutId_);
371 this.receiveInProgress_ = false;
372 if (this.paused_) {
373 this.queuedReceiveError = e;
374 return;
375 }
376 var error = e.error;
377 this.paused_ = true;
378 if (this.onError)
379 this.onError(RECEIVE_ERROR_FROM_MOJO[error]);
380 };
381
382 Connection.prototype.startReceiveTimeoutTimer_ = function() {
383 clearTimeout(this.receiveTimeoutId_);
384 if (this.options_.receiveTimeout && !this.paused_) {
385 this.receiveTimeoutId_ = setTimeout(this.onReceiveTimeout_.bind(this),
386 this.options_.receiveTimeout);
387 }
388 };
389
390 Connection.prototype.onReceiveTimeout_ = function() {
391 if (this.onError)
392 this.onError('timeout');
393 this.startReceiveTimeoutTimer_();
256 }; 394 };
257 395
258 var connections_ = {}; 396 var connections_ = {};
259 var nextConnectionId_ = 0; 397 var nextConnectionId_ = 0;
260 398
261 // Wrap all access to |connections_| through getConnections to avoid adding 399 // Wrap all access to |connections_| through getConnections to avoid adding
262 // any synchronous dependencies on it. This will likely be important when 400 // any synchronous dependencies on it. This will likely be important when
263 // supporting persistent connections by stashing them. 401 // supporting persistent connections by stashing them.
264 function getConnections() { 402 function getConnections() {
265 return Promise.resolve(connections_); 403 return Promise.resolve(connections_);
266 } 404 }
267 405
268 function getConnection(id) { 406 function getConnection(id) {
269 return getConnections().then(function(connections) { 407 return getConnections().then(function(connections) {
270 if (!connections[id]) 408 if (!connections[id])
271 throw new Error ('Serial connection not found.'); 409 throw new Error('Serial connection not found.');
272 return connections[id]; 410 return connections[id];
273 }); 411 });
274 } 412 }
275 413
276 function allocateConnectionId() { 414 function allocateConnectionId() {
277 return Promise.resolve(nextConnectionId_++); 415 return Promise.resolve(nextConnectionId_++);
278 } 416 }
279 417
280 return { 418 return {
281 getDevices: getDevices, 419 getDevices: getDevices,
282 createConnection: Connection.create, 420 createConnection: Connection.create,
283 getConnection: getConnection, 421 getConnection: getConnection,
284 getConnections: getConnections, 422 getConnections: getConnections,
423 // For testing.
424 Connection: Connection,
285 }; 425 };
286 }); 426 });
OLDNEW
« no previous file with comments | « extensions/renderer/resources/serial_custom_bindings.js ('k') | extensions/test/data/serial_unittest.js » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698