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,
24 * @type {remoting.SignalStrategy}
27 this.primary_ = primary;
28 this.primary_.setStateChangedCallback(this.onPrimaryStateChanged_.bind(this));
31 * @type {remoting.SignalStrategy}
34 this.secondary_ = secondary;
35 this.secondary_.setStateChangedCallback(
36 this.onSecondaryStateChanged_.bind(this));
39 * @type {?function(remoting.SignalStrategy.State)}
42 this.onStateChangedCallback_ = null;
45 * @type {?function(Element):void}
48 this.onIncomingStanzaCallback_ = null;
55 this.PRIMARY_CONNECT_TIMEOUT_MS_ = 10 * 1000;
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',
75 this.state_ = this.State.NOT_CONNECTED;
78 * @type {?remoting.SignalStrategy.State}
81 this.externalState_ = null;
105 this.primaryConnectTimerId_ = 0;
108 * @type {remoting.LogToServer}
111 this.logToServer_ = null;
114 * @type {Array<{strategyType: remoting.SignalStrategy.Type,
115 progress: remoting.FallbackSignalStrategy.Progress,
118 this.connectionSetupResults_ = [];
130 remoting.FallbackSignalStrategy.Progress = {
131 SUCCEEDED: 'succeeded',
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.
147 remoting.FallbackSignalStrategy.prototype.setStateChangedCallback = function(
148 onStateChangedCallback) {
149 this.onStateChangedCallback_ = onStateChangedCallback;
153 * @param {?function(Element):void} onIncomingStanzaCallback Callback to call on
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);
169 * @param {string} server
170 * @param {string} username
171 * @param {string} authToken
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
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
203 remoting.FallbackSignalStrategy.prototype.sendConnectionSetupResults =
204 function(logToServer) {
205 this.logToServer_ = logToServer;
206 this.sendConnectionSetupResultsInternal_();
209 remoting.FallbackSignalStrategy.prototype.sendConnectionSetupResultsInternal_ =
211 for (var i = 0; i < this.connectionSetupResults_.length; ++i) {
212 var result = this.connectionSetupResults_[i];
213 this.logToServer_.logSignalStrategyProgress(result.strategyType,
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);
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.
250 remoting.FallbackSignalStrategy.prototype.getConnectedSignalStrategy_ =
252 if (this.state_ == this.State.PRIMARY_SUCCEEDED) {
254 this.primary_.getState() == remoting.SignalStrategy.State.CONNECTED);
255 return this.primary_;
256 } else if (this.state_ == this.State.SECONDARY_SUCCEEDED) {
258 this.secondary_.getState() == remoting.SignalStrategy.State.CONNECTED);
259 return this.secondary_;
263 'getConnectedSignalStrategy called in unconnected state');
269 * @param {remoting.SignalStrategy.State} state
272 remoting.FallbackSignalStrategy.prototype.onPrimaryStateChanged_ =
275 case remoting.SignalStrategy.State.CONNECTED:
276 if (this.state_ == this.State.PRIMARY_PENDING) {
277 window.clearTimeout(this.primaryConnectTimerId_);
278 this.updateProgress_(
280 remoting.FallbackSignalStrategy.Progress.SUCCEEDED);
281 this.state_ = this.State.PRIMARY_SUCCEEDED;
283 this.updateProgress_(
285 remoting.FallbackSignalStrategy.Progress.SUCCEEDED_LATE);
289 case remoting.SignalStrategy.State.FAILED:
290 if (this.state_ == this.State.PRIMARY_PENDING) {
291 window.clearTimeout(this.primaryConnectTimerId_);
292 this.updateProgress_(
294 remoting.FallbackSignalStrategy.Progress.FAILED);
295 this.connectSecondary_();
297 this.updateProgress_(
299 remoting.FallbackSignalStrategy.Progress.FAILED_LATE);
301 return; // Don't notify the external callback
303 case remoting.SignalStrategy.State.CLOSED:
304 this.state_ = this.State.CLOSED;
308 this.notifyExternalCallback_(state);
312 * @param {remoting.SignalStrategy.State} state
315 remoting.FallbackSignalStrategy.prototype.onSecondaryStateChanged_ =
318 case remoting.SignalStrategy.State.CONNECTED:
319 this.updateProgress_(
321 remoting.FallbackSignalStrategy.Progress.SUCCEEDED);
322 this.state_ = this.State.SECONDARY_SUCCEEDED;
325 case remoting.SignalStrategy.State.FAILED:
326 this.updateProgress_(
328 remoting.FallbackSignalStrategy.Progress.FAILED);
329 this.state_ = this.State.SECONDARY_FAILED;
332 case remoting.SignalStrategy.State.CLOSED:
333 this.state_ = this.State.CLOSED;
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
350 remoting.FallbackSignalStrategy.prototype.notifyExternalCallback_ =
352 if (this.externalState_ === null || state > this.externalState_) {
353 this.externalState_ = state;
354 this.onStateChangedCallback_(state);
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_);
376 remoting.FallbackSignalStrategy.prototype.onPrimaryTimeout_ = function() {
377 this.updateProgress_(
379 remoting.FallbackSignalStrategy.Progress.TIMED_OUT);
380 this.connectSecondary_();
384 * @param {remoting.SignalStrategy} strategy
385 * @param {remoting.FallbackSignalStrategy.Progress} progress
388 remoting.FallbackSignalStrategy.prototype.updateProgress_ = function(
389 strategy, progress) {
390 console.log('FallbackSignalStrategy progress: ' + strategy.getType() + ' ' +
392 this.connectionSetupResults_.push({
393 'strategyType': strategy.getType(),
394 'progress': progress,
395 'elapsed': new Date().getTime() - this.startTime_
397 if (this.logToServer_) {
398 this.sendConnectionSetupResultsInternal_();