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_receiver', [
7 'device/serial/data_stream.mojom',
8 'device/serial/data_stream_serialization.mojom',
9 'mojo/public/js/bindings/core',
10 'mojo/public/js/bindings/router',
11 ], function(asyncWaiter, dataStream, serialization, core, router) {
13 * @module data_receiver
17 * A pending receive operation.
19 * @alias module:data_receiver~PendingReceive
22 function PendingReceive() {
24 * The promise that will be resolved or rejected when this receive completes
25 * or fails, respectively.
26 * @type {!Promise.<ArrayBuffer>}
29 this.promise_ = new Promise(function(resolve, reject) {
31 * The callback to call with the data received on success.
35 this.dataCallback_ = resolve;
37 * The callback to call with the error on failure.
41 this.errorCallback_ = reject;
46 * Returns the promise that will be resolved when this operation completes or
47 * rejected if an error occurs.
48 * @return {Promise.<ArrayBuffer>} A promise to the data received.
50 PendingReceive.prototype.getPromise = function() {
55 * Dispatches received data to the promise returned by
56 * [getPromise]{@link module:data_receiver.PendingReceive#getPromise}.
57 * @param {!ArrayBuffer} data The data to dispatch.
59 PendingReceive.prototype.dispatchData = function(data) {
60 this.dataCallback_(data);
64 * Dispatches an error if the offset of the error has been reached.
65 * @param {!PendingReceiveError} error The error to dispatch.
66 * @param {number} bytesReceived The number of bytes that have been received.
68 PendingReceive.prototype.dispatchError = function(error, bytesReceived) {
69 if (bytesReceived != error.offset)
73 e.error = error.error;
74 this.errorCallback_(e);
79 * Unconditionally dispatches an error.
80 * @param {number} error The error to dispatch.
82 PendingReceive.prototype.dispatchFatalError = function(error) {
85 this.errorCallback_(e);
89 * A DataReceiver that receives data from a DataSource.
90 * @param {!MojoHandle} handle The handle to the DataSource.
91 * @param {number} bufferSize How large a buffer the data pipe should use.
92 * @param {number} fatalErrorValue The receive error value to report in the
93 * event of a fatal error.
95 * @alias module:data_receiver.DataReceiver
97 function DataReceiver(handle, bufferSize, fatalErrorValue) {
98 var dataPipeOptions = {
99 flags: core.CREATE_DATA_PIPE_OPTIONS_FLAG_NONE,
101 capacityNumBytes: bufferSize,
103 var receivePipe = core.createDataPipe(dataPipeOptions);
105 handle, receivePipe.consumerHandle, fatalErrorValue, 0, null, false);
106 this.source_.init(receivePipe.producerHandle);
109 DataReceiver.prototype =
110 $Object.create(dataStream.DataSourceClientStub.prototype);
113 * Closes this DataReceiver.
115 DataReceiver.prototype.close = function() {
118 this.shutDown_ = true;
119 this.router_.close();
121 core.close(this.receivePipe_);
123 this.receive_.dispatchFatalError(this.fatalErrorValue_);
124 this.receive_ = null;
129 * Initialize this DataReceiver.
130 * @param {!MojoHandle} source A handle to the DataSource
131 * @param {!MojoHandle} dataPipe A handle to use for receiving data from the
133 * @param {number} fatalErrorValue The error to dispatch in the event of a
135 * @param {number} bytesReceived The number of bytes already received.
136 * @param {PendingReceiveError} pendingError The pending error if there is
138 * @param {boolean} paused Whether the DataSource is paused.
141 DataReceiver.prototype.init_ = function(source,
148 * The [Router]{@link module:mojo/public/js/bindings/router.Router} for the
149 * connection to the DataSource.
152 this.router_ = new router.Router(source);
154 * The connection to the DataSource.
157 this.source_ = new dataStream.DataSourceProxy(this.router_);
158 this.router_.setIncomingReceiver(this);
160 * The handle to the data pipe to use for receiving data.
163 this.receivePipe_ = dataPipe;
165 * The current receive operation.
166 * @type {module:data_receiver~PendingReceive}
169 this.receive_ = null;
171 * The error to be dispatched in the event of a fatal error.
175 this.fatalErrorValue_ = fatalErrorValue;
177 * The async waiter used to wait for
178 * |[receivePipe_]{@link module:data_receiver.DataReceiver#receivePipe_}| to
180 * @type {!module:async_waiter.AsyncWaiter}
183 this.waiter_ = new asyncWaiter.AsyncWaiter(this.receivePipe_,
184 core.HANDLE_SIGNAL_READABLE,
185 this.onHandleReady_.bind(this));
187 * The number of bytes received from the DataSource.
191 this.bytesReceived_ = bytesReceived;
193 * The pending error if there is one.
194 * @type {PendingReceiveError}
197 this.pendingError_ = pendingError;
199 * Whether the DataSource is paused.
203 this.paused_ = paused;
205 * Whether this DataReceiver has shut down.
209 this.shutDown_ = false;
213 * Serializes this DataReceiver.
214 * This will cancel a receive if one is in progress.
215 * @return {!Promise.<SerializedDataReceiver>} A promise that will resolve to
216 * the serialization of this DataReceiver. If this DataReceiver has shut
217 * down, the promise will resolve to null.
219 DataReceiver.prototype.serialize = function() {
221 return Promise.resolve(null);
225 this.receive_.dispatchFatalError(this.fatalErrorValue_);
226 this.receive_ = null;
228 var serialized = new serialization.SerializedDataReceiver();
229 serialized.source = this.router_.connector_.handle_;
230 serialized.data_pipe = this.receivePipe_;
231 serialized.fatal_error_value = this.fatalErrorValue_;
232 serialized.bytes_received = this.bytesReceived_;
233 serialized.paused = this.paused_;
234 serialized.pending_error = this.pendingError_;
235 this.router_.connector_.handle_ = null;
236 this.router_.close();
237 this.shutDown_ = true;
238 return Promise.resolve(serialized);
242 * Deserializes a SerializedDataReceiver.
243 * @param {SerializedDataReceiver} serialized The serialized DataReceiver.
244 * @return {!DataReceiver} The deserialized DataReceiver.
246 DataReceiver.deserialize = function(serialized) {
247 var receiver = $Object.create(DataReceiver.prototype);
248 receiver.deserialize_(serialized);
253 * Deserializes a SerializedDataReceiver into this DataReceiver.
254 * @param {SerializedDataReceiver} serialized The serialized DataReceiver.
257 DataReceiver.prototype.deserialize_ = function(serialized) {
259 this.shutDown_ = true;
262 this.init_(serialized.source,
263 serialized.data_pipe,
264 serialized.fatal_error_value,
265 serialized.bytes_received,
266 serialized.pending_error,
271 * Receive data from the DataSource.
272 * @return {Promise.<ArrayBuffer>} A promise to the received data. If an error
273 * occurs, the promise will reject with an Error object with a property
274 * error containing the error code.
275 * @throws Will throw if this has encountered a fatal error or another receive
278 DataReceiver.prototype.receive = function() {
280 throw new Error('DataReceiver has been closed');
282 throw new Error('Receive already in progress.');
283 var receive = new PendingReceive();
284 var promise = receive.getPromise();
285 if (this.pendingError_ &&
286 receive.dispatchError(this.pendingError_, this.bytesReceived_)) {
287 this.pendingError_ = null;
292 this.source_.resume();
293 this.paused_ = false;
295 this.receive_ = receive;
296 this.waiter_.start();
302 * |[receivePipe_]{@link module:data_receiver.DataReceiver#receivePipe_}| is
303 * ready to read. Reads from the data pipe if the wait is successful.
304 * @param {number} waitResult The result of the asynchronous wait.
307 DataReceiver.prototype.onHandleReady_ = function(waitResult) {
308 if (waitResult != core.RESULT_OK || !this.receive_) {
312 var result = core.readData(this.receivePipe_, core.READ_DATA_FLAG_NONE);
313 if (result.result == core.RESULT_OK) {
314 // TODO(sammc): Handle overflow in the same fashion as the C++ receiver.
315 this.bytesReceived_ += result.buffer.byteLength;
316 this.receive_.dispatchData(result.buffer);
317 this.receive_ = null;
318 } else if (result.result == core.RESULT_SHOULD_WAIT) {
319 this.waiter_.start();
326 * Invoked by the DataSource when an error is encountered.
327 * @param {number} offset The location at which the error occurred.
328 * @param {number} error The error that occurred.
331 DataReceiver.prototype.onError = function(offset, error) {
335 var pendingError = new serialization.PendingReceiveError();
336 pendingError.error = error;
337 pendingError.offset = offset;
339 this.receive_.dispatchError(pendingError, this.bytesReceived_)) {
340 this.receive_ = null;
345 this.pendingError_ = pendingError;
348 return {DataReceiver: DataReceiver};