Update To 11.40.268.0
[platform/framework/web/crosswalk.git] / src / extensions / renderer / resources / data_receiver.js
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_receiver', [
6     'device/serial/data_stream.mojom',
7     'device/serial/data_stream_serialization.mojom',
8     'mojo/public/js/core',
9     'mojo/public/js/router',
10 ], function(dataStream, serialization, core, router) {
11   /**
12    * @module data_receiver
13    */
14
15   /**
16    * A pending receive operation.
17    * @constructor
18    * @alias module:data_receiver~PendingReceive
19    * @private
20    */
21   function PendingReceive() {
22     /**
23      * The promise that will be resolved or rejected when this receive completes
24      * or fails, respectively.
25      * @type {!Promise.<ArrayBuffer>}
26      * @private
27      */
28     this.promise_ = new Promise(function(resolve, reject) {
29       /**
30        * The callback to call with the data received on success.
31        * @type {Function}
32        * @private
33        */
34       this.dataCallback_ = resolve;
35       /**
36        * The callback to call with the error on failure.
37        * @type {Function}
38        * @private
39        */
40       this.errorCallback_ = reject;
41     }.bind(this));
42   }
43
44   /**
45    * Returns the promise that will be resolved when this operation completes or
46    * rejected if an error occurs.
47    * @return {Promise.<ArrayBuffer>} A promise to the data received.
48    */
49   PendingReceive.prototype.getPromise = function() {
50     return this.promise_;
51   };
52
53   /**
54    * Dispatches received data to the promise returned by
55    * [getPromise]{@link module:data_receiver.PendingReceive#getPromise}.
56    * @param {!ArrayBuffer} data The data to dispatch.
57    */
58   PendingReceive.prototype.dispatchData = function(data) {
59     this.dataCallback_(data);
60   };
61
62   /**
63    * Dispatches an error if the offset of the error has been reached.
64    * @param {!PendingReceiveError} error The error to dispatch.
65    * @param {number} bytesReceived The number of bytes that have been received.
66    */
67   PendingReceive.prototype.dispatchError = function(error) {
68     if (error.queuePosition > 0)
69       return false;
70
71     var e = new Error();
72     e.error = error.error;
73     this.errorCallback_(e);
74     return true;
75   };
76
77   /**
78    * Unconditionally dispatches an error.
79    * @param {number} error The error to dispatch.
80    */
81   PendingReceive.prototype.dispatchFatalError = function(error) {
82     var e = new Error();
83     e.error = error;
84     this.errorCallback_(e);
85   };
86
87   /**
88    * A DataReceiver that receives data from a DataSource.
89    * @param {!MojoHandle} handle The handle to the DataSource.
90    * @param {number} bufferSize How large a buffer to use.
91    * @param {number} fatalErrorValue The receive error value to report in the
92    *     event of a fatal error.
93    * @constructor
94    * @alias module:data_receiver.DataReceiver
95    */
96   function DataReceiver(handle, bufferSize, fatalErrorValue) {
97     this.init_(handle, fatalErrorValue, 0, null, [], false);
98     this.source_.init(bufferSize);
99   }
100
101   DataReceiver.prototype =
102       $Object.create(dataStream.DataSourceClient.stubClass.prototype);
103
104   /**
105    * Closes this DataReceiver.
106    */
107   DataReceiver.prototype.close = function() {
108     if (this.shutDown_)
109       return;
110     this.shutDown_ = true;
111     this.router_.close();
112     if (this.receive_) {
113       this.receive_.dispatchFatalError(this.fatalErrorValue_);
114       this.receive_ = null;
115     }
116   };
117
118   /**
119    * Initialize this DataReceiver.
120    * @param {!MojoHandle} source A handle to the DataSource
121    * @param {number} fatalErrorValue The error to dispatch in the event of a
122    *     fatal error.
123    * @param {number} bytesReceived The number of bytes already received.
124    * @param {PendingReceiveError} pendingError The pending error if there is
125    *     one.
126    * @param {!Array.<!ArrayBuffer>} pendingData Data received from the
127    *     DataSource not yet requested by the client.
128    * @param {boolean} paused Whether the DataSource is paused.
129    * @private
130    */
131   DataReceiver.prototype.init_ = function(source,
132                                           fatalErrorValue,
133                                           bytesReceived,
134                                           pendingError,
135                                           pendingData,
136                                           paused) {
137     /**
138      * The [Router]{@link module:mojo/public/js/router.Router} for the
139      * connection to the DataSource.
140      * @private
141      */
142     this.router_ = new router.Router(source);
143     /**
144      * The connection to the DataSource.
145      * @private
146      */
147     this.source_ = new dataStream.DataSource.proxyClass(this.router_);
148     this.router_.setIncomingReceiver(this);
149     /**
150      * The current receive operation.
151      * @type {module:data_receiver~PendingReceive}
152      * @private
153      */
154     this.receive_ = null;
155     /**
156      * The error to be dispatched in the event of a fatal error.
157      * @const {number}
158      * @private
159      */
160     this.fatalErrorValue_ = fatalErrorValue;
161     /**
162      * The pending error if there is one.
163      * @type {PendingReceiveError}
164      * @private
165      */
166     this.pendingError_ = pendingError;
167     /**
168      * Whether the DataSource is paused.
169      * @type {boolean}
170      * @private
171      */
172     this.paused_ = paused;
173     /**
174      * A queue of data that has been received from the DataSource, but not
175      * consumed by the client.
176      * @type {module:data_receiver~PendingData[]}
177      * @private
178      */
179     this.pendingDataBuffers_ = pendingData;
180     /**
181      * Whether this DataReceiver has shut down.
182      * @type {boolean}
183      * @private
184      */
185     this.shutDown_ = false;
186   };
187
188   /**
189    * Serializes this DataReceiver.
190    * This will cancel a receive if one is in progress.
191    * @return {!Promise.<SerializedDataReceiver>} A promise that will resolve to
192    *     the serialization of this DataReceiver. If this DataReceiver has shut
193    *     down, the promise will resolve to null.
194    */
195   DataReceiver.prototype.serialize = function() {
196     if (this.shutDown_)
197       return Promise.resolve(null);
198
199     if (this.receive_) {
200       this.receive_.dispatchFatalError(this.fatalErrorValue_);
201       this.receive_ = null;
202     }
203     var serialized = new serialization.SerializedDataReceiver();
204     serialized.source = this.router_.connector_.handle_;
205     serialized.fatal_error_value = this.fatalErrorValue_;
206     serialized.paused = this.paused_;
207     serialized.pending_error = this.pendingError_;
208     serialized.pending_data = [];
209     $Array.forEach(this.pendingDataBuffers_, function(buffer) {
210       serialized.pending_data.push(new Uint8Array(buffer));
211     });
212     this.router_.connector_.handle_ = null;
213     this.router_.close();
214     this.shutDown_ = true;
215     return Promise.resolve(serialized);
216   };
217
218   /**
219    * Deserializes a SerializedDataReceiver.
220    * @param {SerializedDataReceiver} serialized The serialized DataReceiver.
221    * @return {!DataReceiver} The deserialized DataReceiver.
222    */
223   DataReceiver.deserialize = function(serialized) {
224     var receiver = $Object.create(DataReceiver.prototype);
225     receiver.deserialize_(serialized);
226     return receiver;
227   };
228
229   /**
230    * Deserializes a SerializedDataReceiver into this DataReceiver.
231    * @param {SerializedDataReceiver} serialized The serialized DataReceiver.
232    * @private
233    */
234   DataReceiver.prototype.deserialize_ = function(serialized) {
235     if (!serialized) {
236       this.shutDown_ = true;
237       return;
238     }
239     var pendingData = [];
240     $Array.forEach(serialized.pending_data, function(data) {
241       var buffer = new Uint8Array(data.length);
242       buffer.set(data);
243       pendingData.push(buffer.buffer);
244     });
245     this.init_(serialized.source,
246                serialized.fatal_error_value,
247                serialized.bytes_received,
248                serialized.pending_error,
249                pendingData,
250                serialized.paused);
251   };
252
253   /**
254    * Receive data from the DataSource.
255    * @return {Promise.<ArrayBuffer>} A promise to the received data. If an error
256    *     occurs, the promise will reject with an Error object with a property
257    *     error containing the error code.
258    * @throws Will throw if this has encountered a fatal error or another receive
259    *     is in progress.
260    */
261   DataReceiver.prototype.receive = function() {
262     if (this.shutDown_)
263       throw new Error('DataReceiver has been closed');
264     if (this.receive_)
265       throw new Error('Receive already in progress.');
266     var receive = new PendingReceive();
267     var promise = receive.getPromise();
268     if (this.pendingError_ &&
269         receive.dispatchError(this.pendingError_)) {
270       this.pendingError_ = null;
271       this.paused_ = true;
272       return promise;
273     }
274     if (this.paused_) {
275       this.source_.resume();
276       this.paused_ = false;
277     }
278     this.receive_ = receive;
279     this.dispatchData_();
280     return promise;
281   };
282
283   DataReceiver.prototype.dispatchData_ = function() {
284     if (!this.receive_) {
285       this.close();
286       return;
287     }
288     if (this.pendingDataBuffers_.length) {
289       this.receive_.dispatchData(this.pendingDataBuffers_[0]);
290       this.source_.reportBytesReceived(this.pendingDataBuffers_[0].byteLength);
291       this.receive_ = null;
292       this.pendingDataBuffers_.shift();
293       if (this.pendingError_)
294         this.pendingError_.queuePosition--;
295     }
296   };
297
298   /**
299    * Invoked by the DataSource when an error is encountered.
300    * @param {number} offset The location at which the error occurred.
301    * @param {number} error The error that occurred.
302    * @private
303    */
304   DataReceiver.prototype.onError = function(error) {
305     if (this.shutDown_)
306       return;
307
308     var pendingError = new serialization.PendingReceiveError();
309     pendingError.error = error;
310     pendingError.queuePosition = this.pendingDataBuffers_.length;
311     if (this.receive_ && this.receive_.dispatchError(pendingError)) {
312       this.receive_ = null;
313       this.paused_ = true;
314       return;
315     }
316     this.pendingError_ = pendingError;
317   };
318
319   DataReceiver.prototype.onData = function(data) {
320     var buffer = new ArrayBuffer(data.length);
321     var uintView = new Uint8Array(buffer);
322     uintView.set(data);
323     this.pendingDataBuffers_.push(buffer);
324     if (this.receive_)
325       this.dispatchData_();
326   };
327
328   return {DataReceiver: DataReceiver};
329 });