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} */
80 * @type {Array<{strategyType: remoting.SignalStrategy.Type,
81 progress: remoting.FallbackSignalStrategy.Progress}>}
83 this.connectionSetupResults_ = [];
90 remoting.FallbackSignalStrategy.Progress = {
91 SUCCEEDED: 'succeeded',
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.
107 remoting.FallbackSignalStrategy.prototype.setStateChangedCallback = function(
108 onStateChangedCallback) {
109 this.onStateChangedCallback_ = onStateChangedCallback;
113 * @param {?function(Element):void} onIncomingStanzaCallback Callback to call on
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);
129 * @param {string} server
130 * @param {string} username
131 * @param {string} authToken
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
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
164 remoting.FallbackSignalStrategy.prototype.sendConnectionSetupResults =
165 function(logToServer) {
166 this.logger_ = logToServer;
167 this.sendConnectionSetupResultsInternal_();
170 remoting.FallbackSignalStrategy.prototype.sendConnectionSetupResultsInternal_ =
172 for (var i = 0; i < this.connectionSetupResults_.length; ++i) {
173 var result = this.connectionSetupResults_[i];
174 this.logger_.logSignalStrategyProgress(result.strategyType,
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_ + '.');
192 this.secondary_.getState() == remoting.SignalStrategy.State.FAILED,
193 'getError() called with secondary state ' + this.secondary_.getState() +
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.
213 remoting.FallbackSignalStrategy.prototype.getConnectedSignalStrategy_ =
215 if (this.state_ == this.State.PRIMARY_SUCCEEDED) {
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) {
223 this.secondary_.getState() == remoting.SignalStrategy.State.CONNECTED,
224 'getConnectedSignalStrategy_() called with secondary state ' +
225 this.secondary_.getState() + '.');
226 return this.secondary_;
230 'getConnectedSignalStrategy() called in state ' + this.state_ + '.');
236 * @param {remoting.SignalStrategy.State} state
239 remoting.FallbackSignalStrategy.prototype.onPrimaryStateChanged_ =
242 case remoting.SignalStrategy.State.CONNECTED:
243 if (this.state_ == this.State.PRIMARY_PENDING) {
244 window.clearTimeout(this.primaryConnectTimerId_);
245 this.updateProgress_(
247 remoting.FallbackSignalStrategy.Progress.SUCCEEDED);
248 this.state_ = this.State.PRIMARY_SUCCEEDED;
250 this.updateProgress_(
252 remoting.FallbackSignalStrategy.Progress.SUCCEEDED_LATE);
256 case remoting.SignalStrategy.State.FAILED:
257 if (this.state_ == this.State.PRIMARY_PENDING) {
258 window.clearTimeout(this.primaryConnectTimerId_);
259 this.updateProgress_(
261 remoting.FallbackSignalStrategy.Progress.FAILED);
262 this.connectSecondary_();
264 this.updateProgress_(
266 remoting.FallbackSignalStrategy.Progress.FAILED_LATE);
268 return; // Don't notify the external callback
270 case remoting.SignalStrategy.State.CLOSED:
271 this.state_ = this.State.CLOSED;
275 this.notifyExternalCallback_(state);
279 * @param {remoting.SignalStrategy.State} state
282 remoting.FallbackSignalStrategy.prototype.onSecondaryStateChanged_ =
285 case remoting.SignalStrategy.State.CONNECTED:
286 this.updateProgress_(
288 remoting.FallbackSignalStrategy.Progress.SUCCEEDED);
289 this.state_ = this.State.SECONDARY_SUCCEEDED;
292 case remoting.SignalStrategy.State.FAILED:
293 this.updateProgress_(
295 remoting.FallbackSignalStrategy.Progress.FAILED);
296 this.state_ = this.State.SECONDARY_FAILED;
299 case remoting.SignalStrategy.State.CLOSED:
300 this.state_ = this.State.CLOSED;
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
317 remoting.FallbackSignalStrategy.prototype.notifyExternalCallback_ =
319 if (this.externalState_ === null || state > this.externalState_) {
320 this.externalState_ = state;
321 this.onStateChangedCallback_(state);
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_);
344 remoting.FallbackSignalStrategy.prototype.onPrimaryTimeout_ = function() {
345 this.updateProgress_(
347 remoting.FallbackSignalStrategy.Progress.TIMED_OUT);
348 this.connectSecondary_();
352 * @param {remoting.SignalStrategy} strategy
353 * @param {remoting.FallbackSignalStrategy.Progress} progress
356 remoting.FallbackSignalStrategy.prototype.updateProgress_ = function(
357 strategy, progress) {
358 console.log('FallbackSignalStrategy progress: ' + strategy.getType() + ' ' +
360 this.connectionSetupResults_.push({
361 'strategyType': strategy.getType(),
365 this.sendConnectionSetupResultsInternal_();