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

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

Issue 488003002: Add the JS data pipe client to be used to implement serial send. (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@js-receive-pipe
Patch Set: Created 6 years, 4 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
(Empty)
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.
4
5 define('data_sender', [
6 'async_waiter',
7 'device/serial/data_stream.mojom',
8 'mojo/public/js/bindings/core',
9 'mojo/public/js/bindings/router',
10 ], function(asyncWaiter, dataStreamMojom, core, routerModule) {
11 /**
12 * @module data_sender
13 */
14
15 /**
16 * A pending send operation.
17 * @param {ArrayBuffer} data The data to be sent.
18 * @constructor
19 * @alias module:data_sender~PendingSend
20 * @private
21 */
22 function PendingSend(data) {
23 /**
24 * The data to be sent.
raymes 2014/08/20 03:34:48 nit: Could just add a note that this is the *remai
Sam McNally 2014/08/20 04:19:54 Done.
25 * @type {ArrayBuffer}
26 * @private
27 */
28 this.data_ = data;
29 /**
30 * The length of data to be sent.
31 * @type {number}
32 * @private
33 */
34 this.sendLength_ = data.byteLength;
raymes 2014/08/20 03:34:48 nit, optional: could just call it "length_"
Sam McNally 2014/08/20 04:19:54 Done.
35 /**
36 * The number of bytes that have been received by the DataSink.
37 * @type {number}
38 * @private
39 */
40 this.bytesSent_ = 0;
41 /**
42 * The promise that will be resolved or rejected when this send completes
43 * or fails, respectively.
44 * @type {Promise.<number>}
45 * @private
46 */
47 this.promise_ = new Promise(function(resolve, reject) {
48 /**
49 * The callback to call on success.
50 * @type {Function}
51 * @private
52 */
53 this.successCallback_ = resolve;
54 /**
55 * The callback to call with the error on failure.
56 * @type {Function}
57 * @private
58 */
59 this.errorCallback_ = reject;
60 }.bind(this));
61 }
62
63 /**
64 * Returns the promise that will be resolved when this operation completes or
65 * rejected if an error occurs.
66 * @return {Promise.<number>} A promise to the number of bytes sent.
67 */
68 PendingSend.prototype.getPromise = function() {
69 return this.promise_;
70 };
71
72 /**
73 * @typedef module:data_sender~PendingSend.ReportBytesResult
74 * @property {number} bytesLeft The number of bytes reported that were not
75 * sent as part of the send.
76 * @property {boolean?} done Whether this send has completed.
77 * @property {number?} bytesToFlush The number of bytes to flush in the event
78 * of an error.
79 */
80
81 /**
82 * Invoked when the DataSink reports that bytes have been sent. Resolves the
83 * promise returned by
84 * [getPromise()]{@link module:data_sender~PendingSend#getPromise} once all
85 * bytes have been reported as sent.
86 * @param {number} numBytes The number of bytes sent.
87 * @return {module:data_sender~PendingSend.ReportBytesResult}
88 */
89 PendingSend.prototype.reportBytesSent = function(numBytes) {
90 var result = this.reportBytesSentInternal_(numBytes);
91 if (this.bytesSent_ == this.sendLength_) {
92 result.done = true;
93 this.successCallback_(this.bytesSent_);
94 }
95 return result;
96 };
97
98 /**
99 * Invoked when the DataSink reports an error. Rejects the promise returned by
100 * [getPromise()]{@link module:data_sender~PendingSend#getPromise} unless the
101 * error occurred after this send, that is, unless numBytes is greater than
102 * the nubmer of outstanding bytes.
103 * @param {number} numBytes The number of bytes sent.
104 * @param {number} error The error reported by the DataSink.
105 * @return {module:data_sender~PendingSend.ReportBytesResult}
106 */
107 PendingSend.prototype.reportBytesSentAndError = function(numBytes, error) {
108 var result = this.reportBytesSentInternal_(numBytes);
109 if (result.bytesLeft > 0)
raymes 2014/08/20 03:34:48 A comment here noting that having more bytes left
Sam McNally 2014/08/20 04:19:54 Done.
110 return this.reportBytesSent(result.bytesLeft);
raymes 2014/08/20 03:34:48 Again, I think it's confusing to call reportBytesI
Sam McNally 2014/08/20 04:19:54 Done.
111
112 var e = new Error();
113 e.error = error;
114 e.bytesSent = this.bytesSent_;
115 this.errorCallback_(e);
116 this.done = true;
117 result.bytesToFlush =
118 this.sendLength_ - this.data_.byteLength - this.bytesSent_;
119 return result;
120 };
121
122 /**
123 * Updates the internal state in response to a report from the DataSink.
124 * @param {number} numBytes The number of bytes sent.
125 * @return {module:data_sender~PendingSend.ReportBytesResult}
126 * @private
127 */
128 PendingSend.prototype.reportBytesSentInternal_ = function(numBytes) {
129 this.bytesSent_ += numBytes;
130 var result = {bytesLeft: 0, bytesToFlush: 0};
131 if (this.bytesSent_ > this.sendLength_) {
132 result.bytesLeft = this.bytesSent_ - this.sendLength_;
133 this.bytesSent_ = this.sendLength_;
134 }
135 result.done = false;
136 return result;
137 };
138
139 /**
140 * Writes pending data into the data pipe.
141 * @param {MojoHandle} handle The handle to the data pipe.
142 * @return {number} The Mojo result corresponding to the outcome:
143 * <ul>
144 * <li>RESULT_OK if the write completes successfully;
145 * <li>RESULT_SHOULD_WAIT if some, but not all data was written; or
146 * <li>the data pipe error if the write failed.
147 * </ul>
148 */
149 PendingSend.prototype.sendData = function(handle) {
150 var result = core.writeData(
151 handle, new Int8Array(this.data_), core.WRITE_DATA_FLAG_NONE);
152 if (result.result != core.RESULT_OK)
153 return result.result;
154 this.data_ = this.data_.slice(result.numBytes);
155 return this.data_.byteLength ? core.RESULT_SHOULD_WAIT : core.RESULT_OK;
156 };
157
158 /**
159 * A DataSender that sends data to a DataSink.
160 * @param {MojoHandle} handle The handle to the DataSink.
161 * @param {number} bufferSize How large a buffer the data pipe should use.
162 * @param {number} fatalErrorValue The send error value to report in the
163 * event of a fatal error.
164 * @constructor
165 * @alias module:data_sender.DataSender
166 */
167 function DataSender(handle, bufferSize, fatalErrorValue) {
168 /**
169 * The [Router]{@link module:mojo/public/js/bindings/router.Router} for the
170 * connection to the DataSink.
171 * @private
172 */
173 this.router_ = new routerModule.Router(handle);
174 /**
175 * The connection to the DataSink.
176 * @private
177 */
178 this.sink_ = new dataStreamMojom.DataSinkProxy(this.router_);
179 this.router_.setIncomingReceiver(this);
180 var dataPipeOptions = {
181 flags: core.CREATE_DATA_PIPE_OPTIONS_FLAG_NONE,
182 elementNumBytes: 1,
183 capacityNumBytes: bufferSize,
184 };
185 var sendPipe = core.createDataPipe(dataPipeOptions);
186 this.sink_.init(sendPipe.consumerHandle);
187 /**
188 * The handle to the data pipe to use for sending data.
189 * @private
190 */
191 this.sendPipe_ = sendPipe.producerHandle;
192 /**
193 * The error to be dispatched in the event of a fatal error.
194 * @type {number}
195 * @private
196 */
197 this.fatalErrorValue_ = fatalErrorValue;
198 /**
199 * The async waiter used to wait for
200 * {@link module:data_sender.DataSender#sendPipe_} to be writable.
201 * @type module:async_waiter.AsyncWaiter
202 * @private
203 */
204 this.waiter_ = new asyncWaiter.AsyncWaiter(
205 this.sendPipe_, core.HANDLE_SIGNAL_WRITABLE,
206 this.onHandleReady_.bind(this));
207 /**
208 * A queue of sends that have not fully written their data to the data pipe.
209 * @type module:data_sender~PendingSend[]
210 * @private
211 */
212 this.pendingSends_ = [];
213 /**
214 * A queue of sends that have written their data to the data pipe, but have
215 * not been received by the DataSink.
216 * @type module:data_sender~PendingSend[]
217 * @private
218 */
219 this.sendsAwaitingAck_ = [];
220 /**
221 * The callback that will resolve a pending cancel if one is in progress.
222 * @type Function
223 * @private
224 */
225 this.pendingCancel_ = null;
226 /**
227 * Whether this DataReceiver has shut down.
228 * @type {boolean}
229 * @private
230 */
231 this.shutDown_ = false;
232 }
233
234 DataSender.prototype =
235 $Object.create(dataStreamMojom.DataSinkClientStub.prototype);
236
237 /**
238 * Closes this DataSender.
239 */
240 DataSender.prototype.close = function() {
241 if (this.shutDown_)
242 return;
243 this.shutDown_ = true;
244 this.waiter_.stop();
245 this.router_.close();
246 core.close(this.sendPipe_);
247 while (this.pendingSends_.length) {
248 this.pendingSends_.pop().reportBytesSentAndError(
249 0, this.fatalErrorValue_);
250 }
251 while (this.sendsAwaitingAck_.length) {
252 this.sendsAwaitingAck_.pop().reportBytesSentAndError(
253 0, this.fatalErrorValue_);
254 }
255 if (this.pendingCancel_) {
256 this.pendingCancel_();
257 this.pendingCancel_ = null;
258 }
259 };
260
261 /**
262 * Sends data to the DataSink.
263 * @return {Promise.<number>} A promise to the number of bytes sent. If an
264 * error occurs, the promise will reject with an Error object with a
265 * property error containing the error code.
266 * @throws Will throw if this has encountered a fatal error or a cancel is in
267 * progress.
268 */
269 DataSender.prototype.send = function(data) {
270 if (this.shutDown_)
271 throw new Error('System error');
raymes 2014/08/20 03:34:48 Maybe something more descriptive? :)
Sam McNally 2014/08/20 04:19:54 Done.
272 if (this.pendingCancel_)
273 throw new Error('Cancel in progress');
274 var send = new PendingSend(data);
275 this.pendingSends_.push(send);
276 if (!this.waiter_.isWaiting())
277 this.waiter_.start();
278 return send.getPromise();
279 };
280
281 /**
282 * Requests the cancellation of any in-progress sends. Calls to
283 * [send()]{@link module:data_sender.DataSender#send} will fail until the
284 * cancel has completed.
285 * @param {number} error The error to report for cancelled sends.
286 * @return {Promise} A promise that will resolve when the cancel completes.
287 * @throws Will throw if this has encountered a fatal error or another cancel
288 * is in progress.
289 */
290 DataSender.prototype.cancel = function(error) {
291 if (this.shutDown_)
292 throw new Error('System error');
293 if (this.pendingCancel_)
294 throw new Error('Cancel already in progress');
295 if (this.pendingSends_.length + this.sendsAwaitingAck_.length == 0)
296 return Promise.resolve();
297
298 this.sink_.cancel(error);
299 return new Promise(function(resolve) {
300 this.pendingCancel_ = resolve;
301 }.bind(this));
302 };
303
304 /**
305 * Invoked when |handle_| is ready to write. Writes to the data pipe if the
306 * wait is successful.
307 * @param {number} waitResult The result of the asynchronous wait.
308 * @private
309 */
310 DataSender.prototype.onHandleReady_ = function(result) {
311 this.waiter_.stop();
312 if (this.pendingSends_.length == 0)
313 return;
314 if (result != core.RESULT_OK) {
raymes 2014/08/20 03:34:48 It seems like it could be good to handle this erro
Sam McNally 2014/08/20 04:19:54 Done.
315 this.close();
316 return;
317 }
318 while (this.pendingSends_.length) {
319 var result = this.pendingSends_[0].sendData(this.sendPipe_);
320 if (result == core.RESULT_OK) {
321 this.sendsAwaitingAck_.push(this.pendingSends_.shift());
322 } else if (result == core.RESULT_SHOULD_WAIT) {
323 this.waiter_.start();
324 return;
raymes 2014/08/20 03:34:48 unnecessary return
Sam McNally 2014/08/20 04:19:54 One of either return or break is required.
325 } else {
326 this.close();
327 return;
raymes 2014/08/20 03:34:48 unnecessary return
Sam McNally 2014/08/20 04:19:55 This one is unnecessary because close() clears pen
raymes 2014/08/20 04:29:41 Ahh I missed the fact it is in a loop.
328 }
329 }
330 };
331
332 /**
333 * Calls and clears the pending cancel callback if one is pending.
334 * @private
335 */
336 DataSender.prototype.callCancelCallback_ = function() {
337 if (this.pendingCancel_) {
338 this.pendingCancel_();
339 this.pendingCancel_ = null;
340 }
341 };
342
343 /**
344 * Invoked by the DataSink to report that data has been successfully sent.
345 * @param {number} numBytes The number of bytes sent.
346 * @private
347 */
348 DataSender.prototype.reportBytesSent = function(numBytes) {
349 while (numBytes > 0 && this.sendsAwaitingAck_.length) {
350 var result = this.sendsAwaitingAck_[0].reportBytesSent(numBytes);
351 numBytes = result.bytesLeft;
352 if (result.done)
353 this.sendsAwaitingAck_.shift();
354 }
355 if (numBytes > 0 && this.pendingSends_.length) {
356 var result = this.pendingSends_[0].reportBytesSent(numBytes);
357 numBytes = result.bytesLeft;
358 }
359 if (this.pendingSends_.length + this.sendsAwaitingAck_.length == 0)
raymes 2014/08/20 03:34:48 Maybe a note on why we can call the cancel callbac
Sam McNally 2014/08/20 04:19:54 Done.
360 this.callCancelCallback_();
361 };
362
363 /**
364 * Invoked by the DataSink to report an error in sending data.
365 * @param {number} numBytes The number of bytes sent.
366 * @param {number} error The error reported by the DataSink.
367 * @private
368 */
369 DataSender.prototype.reportBytesSentAndError = function(numBytes, error) {
370 var bytesToFlush = 0;
371 while (this.sendsAwaitingAck_.length) {
372 var result = this.sendsAwaitingAck_[0].reportBytesSentAndError(
373 numBytes, error);
374 numBytes = result.bytesLeft;
375 this.sendsAwaitingAck_.shift();
376 bytesToFlush += result.bytesToFlush;
377 }
378 while (this.pendingSends_.length) {
raymes 2014/08/20 03:34:48 optionally make a note here that only the first pe
Sam McNally 2014/08/20 04:19:54 Done.
379 var result = this.pendingSends_[0].reportBytesSentAndError(
380 numBytes, error);
381 numBytes = result.bytesLeft;
382 this.pendingSends_.shift();
383 bytesToFlush += result.bytesToFlush;
384 }
385 this.callCancelCallback_();
386 return Promise.resolve({bytes_to_flush: bytesToFlush});
387 };
388
389 return {DataSender: DataSender};
390 });
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698