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
3 // found in the LICENSE file.
5 define('data_sender', [
6 'device/serial/data_stream.mojom',
7 'device/serial/data_stream_serialization.mojom',
9 'mojo/public/js/router',
10 ], function(dataStreamMojom
, serialization
, core
, routerModule
) {
16 * A pending send operation.
17 * @param {!ArrayBuffer} data The data to be sent.
19 * @alias module:data_sender~PendingSend
22 function PendingSend(data
) {
24 * The data to be sent.
30 * The total length of data to be sent.
34 this.length_
= data
.byteLength
;
36 * The promise that will be resolved or rejected when this send completes
37 * or fails, respectively.
38 * @type {!Promise<number>}
41 this.promise_
= new Promise(function(resolve
, reject
) {
43 * The callback to call on success.
47 this.successCallback_
= resolve
;
49 * The callback to call with the error on failure.
53 this.errorCallback_
= reject
;
58 * Returns the promise that will be resolved when this operation completes or
59 * rejected if an error occurs.
60 * @return {!Promise<number>} A promise to the number of bytes sent.
62 PendingSend
.prototype.getPromise = function() {
67 * Invoked when the DataSink reports that bytes have been sent. Resolves the
69 * [getPromise()]{@link module:data_sender~PendingSend#getPromise} once all
70 * bytes have been reported as sent.
72 PendingSend
.prototype.reportBytesSent = function() {
73 this.successCallback_(this.length_
);
77 * Invoked when the DataSink reports an error. Rejects the promise returned by
78 * [getPromise()]{@link module:data_sender~PendingSend#getPromise} unless the
79 * error occurred after this send, that is, unless numBytes is greater than
80 * the nubmer of outstanding bytes.
81 * @param {number} numBytes The number of bytes sent.
82 * @param {number} error The error reported by the DataSink.
84 PendingSend
.prototype.reportBytesSentAndError = function(numBytes
, error
) {
87 e
.bytesSent
= numBytes
;
88 this.errorCallback_(e
);
92 * Writes pending data into the data pipe.
93 * @param {!DataSink} sink The DataSink to receive the data.
94 * @return {!Object} result The send result.
95 * @return {boolean} result.completed Whether all of the pending data was
98 PendingSend
.prototype.sendData = function(sink
) {
99 var dataSent
= sink
.onData(new Uint8Array(this.data_
));
105 * A DataSender that sends data to a DataSink.
106 * @param {!MojoHandle} sink The handle to the DataSink.
107 * @param {number} bufferSize How large a buffer to use for data.
108 * @param {number} fatalErrorValue The send error value to report in the
109 * event of a fatal error.
111 * @alias module:data_sender.DataSender
113 function DataSender(sink
, bufferSize
, fatalErrorValue
) {
114 this.init_(sink
, fatalErrorValue
);
118 * Closes this DataSender.
120 DataSender
.prototype.close = function() {
123 this.shutDown_
= true;
124 this.router_
.close();
125 while (this.sendsAwaitingAck_
.length
) {
126 this.sendsAwaitingAck_
.pop().reportBytesSentAndError(
127 0, this.fatalErrorValue_
);
129 this.callCancelCallback_();
133 * Initialize this DataSender.
134 * @param {!MojoHandle} sink A handle to the DataSink.
135 * @param {number} fatalErrorValue The error to dispatch in the event of a
139 DataSender
.prototype.init_ = function(sink
, fatalErrorValue
) {
141 * The error to be dispatched in the event of a fatal error.
145 this.fatalErrorValue_
= fatalErrorValue
;
147 * Whether this DataSender has shut down.
151 this.shutDown_
= false;
153 * The [Router]{@link module:mojo/public/js/router.Router} for the
154 * connection to the DataSink.
157 this.router_
= new routerModule
.Router(sink
);
159 * The connection to the DataSink.
162 this.sink_
= new dataStreamMojom
.DataSink
.proxyClass(this.router_
);
164 * A queue of sends that have sent their data to the DataSink, but have not
165 * been received by the DataSink.
166 * @type {!module:data_sender~PendingSend[]}
169 this.sendsAwaitingAck_
= [];
172 * The callback that will resolve a pending cancel if one is in progress.
176 this.pendingCancel_
= null;
179 * The promise that will be resolved when a pending cancel completes if one
184 this.cancelPromise_
= null;
188 * Serializes this DataSender.
189 * This will cancel any sends in progress before the returned promise
191 * @return {!Promise<SerializedDataSender>} A promise that will resolve to
192 * the serialization of this DataSender. If this DataSender has shut down,
193 * the promise will resolve to null.
195 DataSender
.prototype.serialize = function() {
197 return Promise
.resolve(null);
199 var readyToSerialize
= Promise
.resolve();
200 if (this.sendsAwaitingAck_
.length
) {
201 if (this.pendingCancel_
)
202 readyToSerialize
= this.cancelPromise_
;
204 readyToSerialize
= this.cancel(this.fatalErrorValue_
);
206 return readyToSerialize
.then(function() {
207 var serialized
= new serialization
.SerializedDataSender();
208 serialized
.sink
= this.router_
.connector_
.handle_
;
209 serialized
.fatal_error_value
= this.fatalErrorValue_
;
210 this.router_
.connector_
.handle_
= null;
211 this.router_
.close();
212 this.shutDown_
= true;
218 * Deserializes a SerializedDataSender.
219 * @param {SerializedDataSender} serialized The serialized DataSender.
220 * @return {!DataSender} The deserialized DataSender.
222 DataSender
.deserialize = function(serialized
) {
223 var sender
= $Object
.create(DataSender
.prototype);
224 sender
.deserialize_(serialized
);
229 * Deserializes a SerializedDataSender into this DataSender.
230 * @param {SerializedDataSender} serialized The serialized DataSender.
233 DataSender
.prototype.deserialize_ = function(serialized
) {
235 this.shutDown_
= true;
238 this.init_(serialized
.sink
, serialized
.fatal_error_value
,
239 serialized
.buffer_size
);
243 * Sends data to the DataSink.
244 * @return {!Promise<number>} A promise to the number of bytes sent. If an
245 * error occurs, the promise will reject with an Error object with a
246 * property error containing the error code.
247 * @throws Will throw if this has encountered a fatal error or a cancel is in
250 DataSender
.prototype.send = function(data
) {
252 throw new Error('DataSender has been closed');
253 if (this.pendingCancel_
)
254 throw new Error('Cancel in progress');
255 var send
= new PendingSend(data
);
256 this.sendsAwaitingAck_
.push(send
);
257 send
.sendData(this.sink_
).then(this.reportBytesSentAndError
.bind(this));
258 return send
.getPromise();
262 * Requests the cancellation of any in-progress sends. Calls to
263 * [send()]{@link module:data_sender.DataSender#send} will fail until the
264 * cancel has completed.
265 * @param {number} error The error to report for cancelled sends.
266 * @return {!Promise} A promise that will resolve when the cancel completes.
267 * @throws Will throw if this has encountered a fatal error or another cancel
270 DataSender
.prototype.cancel = function(error
) {
272 throw new Error('DataSender has been closed');
273 if (this.pendingCancel_
)
274 throw new Error('Cancel already in progress');
275 if (this.sendsAwaitingAck_
.length
== 0)
276 return Promise
.resolve();
278 this.sink_
.cancel(error
);
279 this.cancelPromise_
= new Promise(function(resolve
) {
280 this.pendingCancel_
= resolve
;
282 return this.cancelPromise_
;
286 * Calls and clears the pending cancel callback if one is pending.
289 DataSender
.prototype.callCancelCallback_ = function() {
290 if (this.pendingCancel_
) {
291 this.cancelPromise_
= null;
292 this.pendingCancel_();
293 this.pendingCancel_
= null;
298 * Invoked by the DataSink to report that data has been successfully sent.
301 DataSender
.prototype.reportBytesSent = function() {
302 var result
= this.sendsAwaitingAck_
[0].reportBytesSent();
303 this.sendsAwaitingAck_
.shift();
305 // A cancel is completed when all of the sends that were in progress have
306 // completed or failed. This is the case where all sends complete
308 if (this.sendsAwaitingAck_
.length
== 0)
309 this.callCancelCallback_();
313 * Invoked by the DataSink to report an error in sending data.
314 * @param {number} numBytes The number of bytes sent.
315 * @param {number} error The error reported by the DataSink.
318 DataSender
.prototype.reportBytesSentAndError = function(result
) {
319 var numBytes
= result
.bytes_sent
;
320 var error
= result
.error
;
322 this.reportBytesSent();
326 this.sendsAwaitingAck_
[0].reportBytesSentAndError(numBytes
, error
);
327 this.sendsAwaitingAck_
.shift();
328 if (this.sendsAwaitingAck_
.length
)
330 this.callCancelCallback_();
331 this.sink_
.clearError();
334 return {DataSender
: DataSender
};