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.
7 /** @suppress {duplicate} */
8 var remoting
= remoting
|| {};
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|.
15 * @param {remoting.SignalStrategy} primary
16 * @param {remoting.SignalStrategy} secondary
18 * @implements {remoting.SignalStrategy}
21 remoting
.FallbackSignalStrategy = function(primary
,
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;
42 this.PRIMARY_CONNECT_TIMEOUT_MS_
= 10 * 1000;
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',
58 /** @private {string} */
59 this.state_
= this.State
.NOT_CONNECTED
;
61 /** @private {?remoting.SignalStrategy.State} */
62 this.externalState_
= null;
64 /** @private {string} */
67 /** @private {string} */
70 /** @private {string} */
73 /** @private {number} */
74 this.primaryConnectTimerId_
= 0;
76 /** @private {remoting.Logger} */
77 this.logger_
= new remoting
.SessionLogger(
78 remoting
.ChromotingEvent
.Role
.CLIENT
,
79 remoting
.TelemetryEventWriter
.Client
.write
83 * @type {Array<{strategyType: remoting.SignalStrategy.Type,
84 progress: remoting.FallbackSignalStrategy.Progress}>}
86 this.connectionSetupResults_
= [];
93 remoting
.FallbackSignalStrategy
.Progress
= {
94 SUCCEEDED
: 'succeeded',
96 TIMED_OUT
: 'timed-out',
97 SUCCEEDED_LATE
: 'succeeded-late',
98 FAILED_LATE
: 'failed-late',
101 remoting
.FallbackSignalStrategy
.prototype.dispose = function() {
102 this.primary_
.dispose();
103 this.secondary_
.dispose();
107 * @param {function(remoting.SignalStrategy.State):void} onStateChangedCallback
108 * Callback to call on state change.
110 remoting
.FallbackSignalStrategy
.prototype.setStateChangedCallback = function(
111 onStateChangedCallback
) {
112 this.onStateChangedCallback_
= onStateChangedCallback
;
116 * @param {?function(Element):void} onIncomingStanzaCallback Callback to call on
119 remoting
.FallbackSignalStrategy
.prototype.setIncomingStanzaCallback
=
120 function(onIncomingStanzaCallback
) {
121 this.onIncomingStanzaCallback_
= onIncomingStanzaCallback
;
122 if (this.state_
== this.State
.PRIMARY_PENDING
||
123 this.state_
== this.State
.PRIMARY_SUCCEEDED
) {
124 this.primary_
.setIncomingStanzaCallback(onIncomingStanzaCallback
);
125 } else if (this.state_
== this.State
.SECONDARY_PENDING
||
126 this.state_
== this.State
.SECONDARY_SUCCEEDED
) {
127 this.secondary_
.setIncomingStanzaCallback(onIncomingStanzaCallback
);
132 * @param {string} server
133 * @param {string} username
134 * @param {string} authToken
136 remoting
.FallbackSignalStrategy
.prototype.connect
=
137 function(server
, username
, authToken
) {
138 console
.assert(this.state_
== this.State
.NOT_CONNECTED
,
139 'connect() called in state ' + this.state_
+ '.');
140 console
.assert(this.onStateChangedCallback_
!= null,
141 'No state change callback registered.');
142 this.server_
= server
;
143 this.username_
= username
;
144 this.authToken_
= authToken
;
145 this.state_
= this.State
.PRIMARY_PENDING
;
146 this.primary_
.setIncomingStanzaCallback(this.onIncomingStanzaCallback_
);
147 this.primary_
.connect(server
, username
, authToken
);
148 this.primaryConnectTimerId_
=
149 window
.setTimeout(this.onPrimaryTimeout_
.bind(this),
150 this.PRIMARY_CONNECT_TIMEOUT_MS_
);
154 * Sends a message. Can be called only in CONNECTED state.
155 * @param {string} message
157 remoting
.FallbackSignalStrategy
.prototype.sendMessage = function(message
) {
158 this.getConnectedSignalStrategy_().sendMessage(message
);
161 /** @return {remoting.SignalStrategy.State} Current state */
162 remoting
.FallbackSignalStrategy
.prototype.getState = function() {
163 return (this.externalState_
=== null)
164 ? remoting
.SignalStrategy
.State
.NOT_CONNECTED
165 : this.externalState_
;
168 /** @return {!remoting.Error} Error when in FAILED state. */
169 remoting
.FallbackSignalStrategy
.prototype.getError = function() {
170 console
.assert(this.state_
== this.State
.SECONDARY_FAILED
,
171 'getError() called in state ' + this.state_
+ '.');
173 this.secondary_
.getState() == remoting
.SignalStrategy
.State
.FAILED
,
174 'getError() called with secondary state ' + this.secondary_
.getState() +
176 return this.secondary_
.getError();
179 /** @return {string} Current JID when in CONNECTED state. */
180 remoting
.FallbackSignalStrategy
.prototype.getJid = function() {
181 return this.getConnectedSignalStrategy_().getJid();
184 /** @return {remoting.SignalStrategy.Type} The signal strategy type. */
185 remoting
.FallbackSignalStrategy
.prototype.getType = function() {
186 return this.getConnectedSignalStrategy_().getType();
190 * @return {remoting.SignalStrategy} The active signal strategy, if the
191 * connection has succeeded.
194 remoting
.FallbackSignalStrategy
.prototype.getConnectedSignalStrategy_
=
196 if (this.state_
== this.State
.PRIMARY_SUCCEEDED
) {
198 this.primary_
.getState() == remoting
.SignalStrategy
.State
.CONNECTED
,
199 'getConnectedSignalStrategy_() called with primary state ' +
200 this.primary_
.getState() + '.');
201 return this.primary_
;
202 } else if (this.state_
== this.State
.SECONDARY_SUCCEEDED
) {
204 this.secondary_
.getState() == remoting
.SignalStrategy
.State
.CONNECTED
,
205 'getConnectedSignalStrategy_() called with secondary state ' +
206 this.secondary_
.getState() + '.');
207 return this.secondary_
;
211 'getConnectedSignalStrategy() called in state ' + this.state_
+ '.');
217 * @param {remoting.SignalStrategy.State} state
220 remoting
.FallbackSignalStrategy
.prototype.onPrimaryStateChanged_
=
223 case remoting
.SignalStrategy
.State
.CONNECTED
:
224 if (this.state_
== this.State
.PRIMARY_PENDING
) {
225 window
.clearTimeout(this.primaryConnectTimerId_
);
226 this.updateProgress_(
228 remoting
.FallbackSignalStrategy
.Progress
.SUCCEEDED
);
229 this.state_
= this.State
.PRIMARY_SUCCEEDED
;
231 this.updateProgress_(
233 remoting
.FallbackSignalStrategy
.Progress
.SUCCEEDED_LATE
);
237 case remoting
.SignalStrategy
.State
.FAILED
:
238 if (this.state_
== this.State
.PRIMARY_PENDING
) {
239 window
.clearTimeout(this.primaryConnectTimerId_
);
240 this.updateProgress_(
242 remoting
.FallbackSignalStrategy
.Progress
.FAILED
);
243 this.connectSecondary_();
245 this.updateProgress_(
247 remoting
.FallbackSignalStrategy
.Progress
.FAILED_LATE
);
249 return; // Don't notify the external callback
251 case remoting
.SignalStrategy
.State
.CLOSED
:
252 this.state_
= this.State
.CLOSED
;
256 this.notifyExternalCallback_(state
);
260 * @param {remoting.SignalStrategy.State} state
263 remoting
.FallbackSignalStrategy
.prototype.onSecondaryStateChanged_
=
266 case remoting
.SignalStrategy
.State
.CONNECTED
:
267 this.updateProgress_(
269 remoting
.FallbackSignalStrategy
.Progress
.SUCCEEDED
);
270 this.state_
= this.State
.SECONDARY_SUCCEEDED
;
273 case remoting
.SignalStrategy
.State
.FAILED
:
274 this.updateProgress_(
276 remoting
.FallbackSignalStrategy
.Progress
.FAILED
);
277 this.state_
= this.State
.SECONDARY_FAILED
;
280 case remoting
.SignalStrategy
.State
.CLOSED
:
281 this.state_
= this.State
.CLOSED
;
285 this.notifyExternalCallback_(state
);
289 * Notify the external callback of a change in state if it's consistent with
290 * the allowed state transitions (ie, if it represents a later stage in the
291 * connection process). Suppress state transitions that would violate this,
292 * for example a CONNECTING -> NOT_CONNECTED transition when we switch from
293 * the primary to the secondary signal strategy.
295 * @param {remoting.SignalStrategy.State} state
298 remoting
.FallbackSignalStrategy
.prototype.notifyExternalCallback_
=
300 if (this.externalState_
=== null || state
> this.externalState_
) {
301 this.externalState_
= state
;
302 this.onStateChangedCallback_(state
);
309 remoting
.FallbackSignalStrategy
.prototype.connectSecondary_ = function() {
310 console
.assert(this.state_
== this.State
.PRIMARY_PENDING
,
311 'connectSecondary_() called in state ' + this.state_
+ '.');
312 console
.assert(this.server_
!= '', 'No server address set.');
313 console
.assert(this.username_
!= '', 'No username set.');
314 console
.assert(this.authToken_
!= '', 'No auth token set.');
316 this.state_
= this.State
.SECONDARY_PENDING
;
317 this.primary_
.setIncomingStanzaCallback(null);
318 this.secondary_
.setIncomingStanzaCallback(this.onIncomingStanzaCallback_
);
319 this.secondary_
.connect(this.server_
, this.username_
, this.authToken_
);
325 remoting
.FallbackSignalStrategy
.prototype.onPrimaryTimeout_ = function() {
326 this.updateProgress_(
328 remoting
.FallbackSignalStrategy
.Progress
.TIMED_OUT
);
329 this.connectSecondary_();
333 * @param {remoting.SignalStrategy} strategy
334 * @param {remoting.FallbackSignalStrategy.Progress} progress
337 remoting
.FallbackSignalStrategy
.prototype.updateProgress_ = function(
338 strategy
, progress
) {
339 console
.log('FallbackSignalStrategy progress: ' + strategy
.getType() + ' ' +
341 this.logger_
.logSignalStrategyProgress(strategy
.getType(), progress
);