Pin Chrome's shortcut to the Win10 Start menu on install and OS upgrade.
[chromium-blink-merge.git] / remoting / webapp / base / js / fallback_signal_strategy.js
blobe6656f19eb29aba6fb2d425380f966300eca7ab1
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   /** @private {remoting.SignalStrategy} */
24   this.primary_ = primary;
25   this.primary_.setStateChangedCallback(this.onPrimaryStateChanged_.bind(this));
27   /** @private {remoting.SignalStrategy} */
28   this.secondary_ = secondary;
29   this.secondary_.setStateChangedCallback(
30       this.onSecondaryStateChanged_.bind(this));
32   /** @private {?function(remoting.SignalStrategy.State)} */
33   this.onStateChangedCallback_ = null;
35   /** @private {?function(Element):void} */
36   this.onIncomingStanzaCallback_ = null;
38   /**
39    * @private {number}
40    * @const
41    */
42   this.PRIMARY_CONNECT_TIMEOUT_MS_ = 10 * 1000;
44   /**
45    * @enum {string}
46    * @private
47    */
48   this.State = {
49     NOT_CONNECTED: 'not-connected',
50     PRIMARY_PENDING: 'primary-pending',
51     PRIMARY_SUCCEEDED: 'primary-succeeded',
52     SECONDARY_PENDING: 'secondary-pending',
53     SECONDARY_SUCCEEDED: 'secondary-succeeded',
54     SECONDARY_FAILED: 'secondary-failed',
55     CLOSED: 'closed'
56   };
58   /** @private {string} */
59   this.state_ = this.State.NOT_CONNECTED;
61   /** @private {?remoting.SignalStrategy.State} */
62   this.externalState_ = null;
64   /** @private {string} */
65   this.server_ = '';
67   /** @private {string} */
68   this.username_ = '';
70   /** @private {string} */
71   this.authToken_ = '';
73   /** @private {number} */
74   this.primaryConnectTimerId_ = 0;
76   /** @private {remoting.Logger} */
77   this.logger_ = null;
79   /**
80    * @type {Array<{strategyType: remoting.SignalStrategy.Type,
81                     progress: remoting.FallbackSignalStrategy.Progress}>}
82    */
83   this.connectionSetupResults_ = [];
87 /**
88  * @enum {string}
89  */
90 remoting.FallbackSignalStrategy.Progress = {
91   SUCCEEDED: 'succeeded',
92   FAILED: 'failed',
93   TIMED_OUT: 'timed-out',
94   SUCCEEDED_LATE: 'succeeded-late',
95   FAILED_LATE: 'failed-late',
98 remoting.FallbackSignalStrategy.prototype.dispose = function() {
99   this.primary_.dispose();
100   this.secondary_.dispose();
104  * @param {function(remoting.SignalStrategy.State):void} onStateChangedCallback
105  *   Callback to call on state change.
106  */
107 remoting.FallbackSignalStrategy.prototype.setStateChangedCallback = function(
108     onStateChangedCallback) {
109   this.onStateChangedCallback_ = onStateChangedCallback;
113  * @param {?function(Element):void} onIncomingStanzaCallback Callback to call on
114  *     incoming messages.
115  */
116 remoting.FallbackSignalStrategy.prototype.setIncomingStanzaCallback =
117     function(onIncomingStanzaCallback) {
118   this.onIncomingStanzaCallback_ = onIncomingStanzaCallback;
119   if (this.state_ == this.State.PRIMARY_PENDING ||
120       this.state_ == this.State.PRIMARY_SUCCEEDED) {
121     this.primary_.setIncomingStanzaCallback(onIncomingStanzaCallback);
122   } else if (this.state_ == this.State.SECONDARY_PENDING ||
123              this.state_ == this.State.SECONDARY_SUCCEEDED) {
124     this.secondary_.setIncomingStanzaCallback(onIncomingStanzaCallback);
125   }
129  * @param {string} server
130  * @param {string} username
131  * @param {string} authToken
132  */
133 remoting.FallbackSignalStrategy.prototype.connect =
134     function(server, username, authToken) {
135   console.assert(this.state_ == this.State.NOT_CONNECTED,
136                 'connect() called in state ' + this.state_ + '.');
137   console.assert(this.onStateChangedCallback_ != null,
138                  'No state change callback registered.');
139   this.server_ = server;
140   this.username_ = username;
141   this.authToken_ = authToken;
142   this.state_ = this.State.PRIMARY_PENDING;
143   this.primary_.setIncomingStanzaCallback(this.onIncomingStanzaCallback_);
144   this.primary_.connect(server, username, authToken);
145   this.primaryConnectTimerId_ =
146       window.setTimeout(this.onPrimaryTimeout_.bind(this),
147                         this.PRIMARY_CONNECT_TIMEOUT_MS_);
151  * Sends a message. Can be called only in CONNECTED state.
152  * @param {string} message
153  */
154 remoting.FallbackSignalStrategy.prototype.sendMessage = function(message) {
155   this.getConnectedSignalStrategy_().sendMessage(message);
159  * Send any messages accumulated during connection set-up.
161  * @param {remoting.Logger} logToServer The Logger instance for the
162  *     connection.
163  */
164 remoting.FallbackSignalStrategy.prototype.sendConnectionSetupResults =
165     function(logToServer) {
166   this.logger_ = logToServer;
167   this.sendConnectionSetupResultsInternal_();
170 remoting.FallbackSignalStrategy.prototype.sendConnectionSetupResultsInternal_ =
171     function() {
172   for (var i = 0; i < this.connectionSetupResults_.length; ++i) {
173     var result = this.connectionSetupResults_[i];
174     this.logger_.logSignalStrategyProgress(result.strategyType,
175                                                 result.progress);
176   }
177   this.connectionSetupResults_ = [];
180 /** @return {remoting.SignalStrategy.State} Current state */
181 remoting.FallbackSignalStrategy.prototype.getState = function() {
182   return (this.externalState_ === null)
183       ? remoting.SignalStrategy.State.NOT_CONNECTED
184       : this.externalState_;
187 /** @return {!remoting.Error} Error when in FAILED state. */
188 remoting.FallbackSignalStrategy.prototype.getError = function() {
189   console.assert(this.state_ == this.State.SECONDARY_FAILED,
190                 'getError() called in state ' + this.state_ + '.');
191   console.assert(
192       this.secondary_.getState() == remoting.SignalStrategy.State.FAILED,
193       'getError() called with secondary state ' + this.secondary_.getState() +
194       '.');
195   return this.secondary_.getError();
198 /** @return {string} Current JID when in CONNECTED state. */
199 remoting.FallbackSignalStrategy.prototype.getJid = function() {
200   return this.getConnectedSignalStrategy_().getJid();
203 /** @return {remoting.SignalStrategy.Type} The signal strategy type. */
204 remoting.FallbackSignalStrategy.prototype.getType = function() {
205   return this.getConnectedSignalStrategy_().getType();
209  * @return {remoting.SignalStrategy} The active signal strategy, if the
210  *     connection has succeeded.
211  * @private
212  */
213 remoting.FallbackSignalStrategy.prototype.getConnectedSignalStrategy_ =
214     function() {
215   if (this.state_ == this.State.PRIMARY_SUCCEEDED) {
216     console.assert(
217         this.primary_.getState() == remoting.SignalStrategy.State.CONNECTED,
218         'getConnectedSignalStrategy_() called with primary state ' +
219         this.primary_.getState() + '.');
220     return this.primary_;
221   } else if (this.state_ == this.State.SECONDARY_SUCCEEDED) {
222     console.assert(
223         this.secondary_.getState() == remoting.SignalStrategy.State.CONNECTED,
224         'getConnectedSignalStrategy_() called with secondary state ' +
225         this.secondary_.getState() + '.');
226     return this.secondary_;
227   } else {
228     console.assert(
229         false,
230         'getConnectedSignalStrategy() called in state ' + this.state_ + '.');
231     return null;
232   }
236  * @param {remoting.SignalStrategy.State} state
237  * @private
238  */
239 remoting.FallbackSignalStrategy.prototype.onPrimaryStateChanged_ =
240     function(state) {
241   switch (state) {
242     case remoting.SignalStrategy.State.CONNECTED:
243       if (this.state_ == this.State.PRIMARY_PENDING) {
244         window.clearTimeout(this.primaryConnectTimerId_);
245         this.updateProgress_(
246             this.primary_,
247             remoting.FallbackSignalStrategy.Progress.SUCCEEDED);
248         this.state_ = this.State.PRIMARY_SUCCEEDED;
249       } else {
250         this.updateProgress_(
251             this.primary_,
252             remoting.FallbackSignalStrategy.Progress.SUCCEEDED_LATE);
253       }
254       break;
256     case remoting.SignalStrategy.State.FAILED:
257       if (this.state_ == this.State.PRIMARY_PENDING) {
258         window.clearTimeout(this.primaryConnectTimerId_);
259         this.updateProgress_(
260             this.primary_,
261             remoting.FallbackSignalStrategy.Progress.FAILED);
262         this.connectSecondary_();
263       } else {
264         this.updateProgress_(
265             this.primary_,
266             remoting.FallbackSignalStrategy.Progress.FAILED_LATE);
267       }
268       return;  // Don't notify the external callback
270     case remoting.SignalStrategy.State.CLOSED:
271       this.state_ = this.State.CLOSED;
272       break;
273   }
275   this.notifyExternalCallback_(state);
279  * @param {remoting.SignalStrategy.State} state
280  * @private
281  */
282 remoting.FallbackSignalStrategy.prototype.onSecondaryStateChanged_ =
283     function(state) {
284   switch (state) {
285     case remoting.SignalStrategy.State.CONNECTED:
286       this.updateProgress_(
287           this.secondary_,
288           remoting.FallbackSignalStrategy.Progress.SUCCEEDED);
289       this.state_ = this.State.SECONDARY_SUCCEEDED;
290       break;
292     case remoting.SignalStrategy.State.FAILED:
293       this.updateProgress_(
294           this.secondary_,
295           remoting.FallbackSignalStrategy.Progress.FAILED);
296       this.state_ = this.State.SECONDARY_FAILED;
297       break;
299     case remoting.SignalStrategy.State.CLOSED:
300       this.state_ = this.State.CLOSED;
301       break;
302   }
304   this.notifyExternalCallback_(state);
308  * Notify the external callback of a change in state if it's consistent with
309  * the allowed state transitions (ie, if it represents a later stage in the
310  * connection process). Suppress state transitions that would violate this,
311  * for example a CONNECTING -> NOT_CONNECTED transition when we switch from
312  * the primary to the secondary signal strategy.
314  * @param {remoting.SignalStrategy.State} state
315  * @private
316  */
317 remoting.FallbackSignalStrategy.prototype.notifyExternalCallback_ =
318     function(state) {
319   if (this.externalState_ === null || state > this.externalState_) {
320     this.externalState_ = state;
321     this.onStateChangedCallback_(state);
322   }
326  * @private
327  */
328 remoting.FallbackSignalStrategy.prototype.connectSecondary_ = function() {
329   console.assert(this.state_ == this.State.PRIMARY_PENDING,
330                 'connectSecondary_() called in state ' + this.state_ + '.');
331   console.assert(this.server_ != '', 'No server address set.');
332   console.assert(this.username_ != '', 'No username set.');
333   console.assert(this.authToken_ != '', 'No auth token set.');
335   this.state_ = this.State.SECONDARY_PENDING;
336   this.primary_.setIncomingStanzaCallback(null);
337   this.secondary_.setIncomingStanzaCallback(this.onIncomingStanzaCallback_);
338   this.secondary_.connect(this.server_, this.username_, this.authToken_);
342  * @private
343  */
344 remoting.FallbackSignalStrategy.prototype.onPrimaryTimeout_ = function() {
345   this.updateProgress_(
346       this.primary_,
347       remoting.FallbackSignalStrategy.Progress.TIMED_OUT);
348   this.connectSecondary_();
352  * @param {remoting.SignalStrategy} strategy
353  * @param {remoting.FallbackSignalStrategy.Progress} progress
354  * @private
355  */
356 remoting.FallbackSignalStrategy.prototype.updateProgress_ = function(
357     strategy, progress) {
358   console.log('FallbackSignalStrategy progress: ' + strategy.getType() + ' ' +
359       progress);
360   this.connectionSetupResults_.push({
361     'strategyType': strategy.getType(),
362     'progress': progress
363   });
364   if (this.logger_) {
365     this.sendConnectionSetupResultsInternal_();
366   }