Rewrite AndroidSyncSettings to be significantly simpler.
[chromium-blink-merge.git] / remoting / webapp / crd / js / fallback_signal_strategy.js
blob030bfb42729197a4e735299d489d7490f788ff38
1 // Copyright 2015 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 'use strict';
7 /** @suppress {duplicate} */
8 var remoting = remoting || {};
10 /**
11  * A signal strategy encapsulating a primary and a back-up strategy. If the
12  * primary fails or times out, then the secondary is used. Information about
13  * which strategy was used, and why, is returned via |onProgressCallback|.
14  *
15  * @param {remoting.SignalStrategy} primary
16  * @param {remoting.SignalStrategy} secondary
17  *
18  * @implements {remoting.SignalStrategy}
19  * @constructor
20  */
21 remoting.FallbackSignalStrategy = function(primary,
22                                            secondary) {
23   /**
24    * @type {remoting.SignalStrategy}
25    * @private
26    */
27   this.primary_ = primary;
28   this.primary_.setStateChangedCallback(this.onPrimaryStateChanged_.bind(this));
30   /**
31    * @type {remoting.SignalStrategy}
32    * @private
33    */
34   this.secondary_ = secondary;
35   this.secondary_.setStateChangedCallback(
36       this.onSecondaryStateChanged_.bind(this));
38   /**
39    * @type {?function(remoting.SignalStrategy.State)}
40    * @private
41    */
42   this.onStateChangedCallback_ = null;
44   /**
45    * @type {?function(Element):void}
46    * @private
47    */
48   this.onIncomingStanzaCallback_ = null;
50   /**
51    * @type {number}
52    * @private
53    * @const
54    */
55   this.PRIMARY_CONNECT_TIMEOUT_MS_ = 10 * 1000;
57   /**
58    * @enum {string}
59    * @private
60    */
61   this.State = {
62     NOT_CONNECTED: 'not-connected',
63     PRIMARY_PENDING: 'primary-pending',
64     PRIMARY_SUCCEEDED: 'primary-succeeded',
65     SECONDARY_PENDING: 'secondary-pending',
66     SECONDARY_SUCCEEDED: 'secondary-succeeded',
67     SECONDARY_FAILED: 'secondary-failed',
68     CLOSED: 'closed'
69   };
71   /**
72    * @type {string}
73    * @private
74    */
75   this.state_ = this.State.NOT_CONNECTED;
77   /**
78    * @type {?remoting.SignalStrategy.State}
79    * @private
80    */
81   this.externalState_ = null;
83   /**
84    * @type {string}
85    * @private
86    */
87   this.server_ = '';
89   /**
90    * @type {string}
91    * @private
92    */
93   this.username_ = '';
95   /**
96    * @type {string}
97    * @private
98    */
99   this.authToken_ = '';
101   /**
102    * @type {number}
103    * @private
104    */
105   this.primaryConnectTimerId_ = 0;
107   /**
108    * @type {remoting.LogToServer}
109    * @private
110    */
111   this.logToServer_ = null;
113   /**
114    * @type {Array<{strategyType: remoting.SignalStrategy.Type,
115                     progress: remoting.FallbackSignalStrategy.Progress,
116    *                elapsed: number}>}
117    */
118   this.connectionSetupResults_ = [];
120   /**
121    * @type {number}
122    * @private
123    */
124   this.startTime_ = 0;
128  * @enum {string}
129  */
130 remoting.FallbackSignalStrategy.Progress = {
131   SUCCEEDED: 'succeeded',
132   FAILED: 'failed',
133   TIMED_OUT: 'timed-out',
134   SUCCEEDED_LATE: 'succeeded-late',
135   FAILED_LATE: 'failed-late',
138 remoting.FallbackSignalStrategy.prototype.dispose = function() {
139   this.primary_.dispose();
140   this.secondary_.dispose();
144  * @param {function(remoting.SignalStrategy.State):void} onStateChangedCallback
145  *   Callback to call on state change.
146  */
147 remoting.FallbackSignalStrategy.prototype.setStateChangedCallback = function(
148     onStateChangedCallback) {
149   this.onStateChangedCallback_ = onStateChangedCallback;
153  * @param {?function(Element):void} onIncomingStanzaCallback Callback to call on
154  *     incoming messages.
155  */
156 remoting.FallbackSignalStrategy.prototype.setIncomingStanzaCallback =
157     function(onIncomingStanzaCallback) {
158   this.onIncomingStanzaCallback_ = onIncomingStanzaCallback;
159   if (this.state_ == this.State.PRIMARY_PENDING ||
160       this.state_ == this.State.PRIMARY_SUCCEEDED) {
161     this.primary_.setIncomingStanzaCallback(onIncomingStanzaCallback);
162   } else if (this.state_ == this.State.SECONDARY_PENDING ||
163              this.state_ == this.State.SECONDARY_SUCCEEDED) {
164     this.secondary_.setIncomingStanzaCallback(onIncomingStanzaCallback);
165   }
169  * @param {string} server
170  * @param {string} username
171  * @param {string} authToken
172  */
173 remoting.FallbackSignalStrategy.prototype.connect =
174     function(server, username, authToken) {
175   base.debug.assert(this.state_ == this.State.NOT_CONNECTED);
176   base.debug.assert(this.onStateChangedCallback_ != null);
177   this.server_ = server;
178   this.username_ = username;
179   this.authToken_ = authToken;
180   this.state_ = this.State.PRIMARY_PENDING;
181   this.startTime_ = new Date().getTime();
182   this.primary_.setIncomingStanzaCallback(this.onIncomingStanzaCallback_);
183   this.primary_.connect(server, username, authToken);
184   this.primaryConnectTimerId_ =
185       window.setTimeout(this.onPrimaryTimeout_.bind(this),
186                         this.PRIMARY_CONNECT_TIMEOUT_MS_);
190  * Sends a message. Can be called only in CONNECTED state.
191  * @param {string} message
192  */
193 remoting.FallbackSignalStrategy.prototype.sendMessage = function(message) {
194   this.getConnectedSignalStrategy_().sendMessage(message);
198  * Send any messages accumulated during connection set-up.
200  * @param {remoting.LogToServer} logToServer The LogToServer instance for the
201  *     connection.
202  */
203 remoting.FallbackSignalStrategy.prototype.sendConnectionSetupResults =
204     function(logToServer) {
205   this.logToServer_ = logToServer;
206   this.sendConnectionSetupResultsInternal_();
209 remoting.FallbackSignalStrategy.prototype.sendConnectionSetupResultsInternal_ =
210     function() {
211   for (var i = 0; i < this.connectionSetupResults_.length; ++i) {
212     var result = this.connectionSetupResults_[i];
213     this.logToServer_.logSignalStrategyProgress(result.strategyType,
214                                                 result.progress,
215                                                 result.elapsed);
216   }
217   this.connectionSetupResults_ = [];
220 /** @return {remoting.SignalStrategy.State} Current state */
221 remoting.FallbackSignalStrategy.prototype.getState = function() {
222   return (this.externalState_ === null)
223       ? remoting.SignalStrategy.State.NOT_CONNECTED
224       : this.externalState_;
227 /** @return {remoting.Error} Error when in FAILED state. */
228 remoting.FallbackSignalStrategy.prototype.getError = function() {
229   base.debug.assert(this.state_ == this.State.SECONDARY_FAILED);
230   base.debug.assert(
231       this.secondary_.getState() == remoting.SignalStrategy.State.FAILED);
232   return this.secondary_.getError();
235 /** @return {string} Current JID when in CONNECTED state. */
236 remoting.FallbackSignalStrategy.prototype.getJid = function() {
237   return this.getConnectedSignalStrategy_().getJid();
240 /** @return {remoting.SignalStrategy.Type} The signal strategy type. */
241 remoting.FallbackSignalStrategy.prototype.getType = function() {
242   return this.getConnectedSignalStrategy_().getType();
246  * @return {remoting.SignalStrategy} The active signal strategy, if the
247  *     connection has succeeded.
248  * @private
249  */
250 remoting.FallbackSignalStrategy.prototype.getConnectedSignalStrategy_ =
251     function() {
252   if (this.state_ == this.State.PRIMARY_SUCCEEDED) {
253     base.debug.assert(
254         this.primary_.getState() == remoting.SignalStrategy.State.CONNECTED);
255     return this.primary_;
256   } else if (this.state_ == this.State.SECONDARY_SUCCEEDED) {
257     base.debug.assert(
258         this.secondary_.getState() == remoting.SignalStrategy.State.CONNECTED);
259     return this.secondary_;
260   } else {
261     base.debug.assert(
262         false,
263         'getConnectedSignalStrategy called in unconnected state');
264     return null;
265   }
269  * @param {remoting.SignalStrategy.State} state
270  * @private
271  */
272 remoting.FallbackSignalStrategy.prototype.onPrimaryStateChanged_ =
273     function(state) {
274   switch (state) {
275     case remoting.SignalStrategy.State.CONNECTED:
276       if (this.state_ == this.State.PRIMARY_PENDING) {
277         window.clearTimeout(this.primaryConnectTimerId_);
278         this.updateProgress_(
279             this.primary_,
280             remoting.FallbackSignalStrategy.Progress.SUCCEEDED);
281         this.state_ = this.State.PRIMARY_SUCCEEDED;
282       } else {
283         this.updateProgress_(
284             this.primary_,
285             remoting.FallbackSignalStrategy.Progress.SUCCEEDED_LATE);
286       }
287       break;
289     case remoting.SignalStrategy.State.FAILED:
290       if (this.state_ == this.State.PRIMARY_PENDING) {
291         window.clearTimeout(this.primaryConnectTimerId_);
292         this.updateProgress_(
293             this.primary_,
294             remoting.FallbackSignalStrategy.Progress.FAILED);
295         this.connectSecondary_();
296       } else {
297         this.updateProgress_(
298             this.primary_,
299             remoting.FallbackSignalStrategy.Progress.FAILED_LATE);
300       }
301       return;  // Don't notify the external callback
303     case remoting.SignalStrategy.State.CLOSED:
304       this.state_ = this.State.CLOSED;
305       break;
306   }
308   this.notifyExternalCallback_(state);
312  * @param {remoting.SignalStrategy.State} state
313  * @private
314  */
315 remoting.FallbackSignalStrategy.prototype.onSecondaryStateChanged_ =
316     function(state) {
317   switch (state) {
318     case remoting.SignalStrategy.State.CONNECTED:
319       this.updateProgress_(
320           this.secondary_,
321           remoting.FallbackSignalStrategy.Progress.SUCCEEDED);
322       this.state_ = this.State.SECONDARY_SUCCEEDED;
323       break;
325     case remoting.SignalStrategy.State.FAILED:
326       this.updateProgress_(
327           this.secondary_,
328           remoting.FallbackSignalStrategy.Progress.FAILED);
329       this.state_ = this.State.SECONDARY_FAILED;
330       break;
332     case remoting.SignalStrategy.State.CLOSED:
333       this.state_ = this.State.CLOSED;
334       break;
335   }
337   this.notifyExternalCallback_(state);
341  * Notify the external callback of a change in state if it's consistent with
342  * the allowed state transitions (ie, if it represents a later stage in the
343  * connection process). Suppress state transitions that would violate this,
344  * for example a CONNECTING -> NOT_CONNECTED transition when we switch from
345  * the primary to the secondary signal strategy.
347  * @param {remoting.SignalStrategy.State} state
348  * @private
349  */
350 remoting.FallbackSignalStrategy.prototype.notifyExternalCallback_ =
351     function(state) {
352   if (this.externalState_ === null || state > this.externalState_) {
353     this.externalState_ = state;
354     this.onStateChangedCallback_(state);
355   }
359  * @private
360  */
361 remoting.FallbackSignalStrategy.prototype.connectSecondary_ = function() {
362   base.debug.assert(this.state_ == this.State.PRIMARY_PENDING);
363   base.debug.assert(this.server_ != '');
364   base.debug.assert(this.username_ != '');
365   base.debug.assert(this.authToken_ != '');
367   this.state_ = this.State.SECONDARY_PENDING;
368   this.primary_.setIncomingStanzaCallback(null);
369   this.secondary_.setIncomingStanzaCallback(this.onIncomingStanzaCallback_);
370   this.secondary_.connect(this.server_, this.username_, this.authToken_);
374  * @private
375  */
376 remoting.FallbackSignalStrategy.prototype.onPrimaryTimeout_ = function() {
377   this.updateProgress_(
378       this.primary_,
379       remoting.FallbackSignalStrategy.Progress.TIMED_OUT);
380   this.connectSecondary_();
384  * @param {remoting.SignalStrategy} strategy
385  * @param {remoting.FallbackSignalStrategy.Progress} progress
386  * @private
387  */
388 remoting.FallbackSignalStrategy.prototype.updateProgress_ = function(
389     strategy, progress) {
390   console.log('FallbackSignalStrategy progress: ' + strategy.getType() + ' ' +
391       progress);
392   this.connectionSetupResults_.push({
393     'strategyType': strategy.getType(),
394     'progress': progress,
395     'elapsed': new Date().getTime() - this.startTime_
396   });
397   if (this.logToServer_) {
398     this.sendConnectionSetupResultsInternal_();
399   }