// Copyright 2014 The Chromium Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. /** * @fileoverview * Class handling reconnecting the session when it is disconnected due to * network failure. * * The SmartReconnector listens for changes in connection state of * |clientSession| to determine if a reconnection is needed. It then calls into * |connector| to reconnect the session. */ 'use strict'; /** @suppress {duplicate} */ var remoting = remoting || {}; /** * @constructor * @param {remoting.SessionConnector} connector This is used to reconnect the * the session when necessary * @param {remoting.ClientSession} clientSession This represents the current * remote desktop connection. It is used to monitor the changes in * connection state. * @implements {base.Disposable} */ remoting.SmartReconnector = function(connector, clientSession) { /** @private */ this.connector_ = connector; /** @private */ this.clientSession_ = clientSession; /** @private */ this.reconnectTimerId_ = null; /** @private */ this.connectionTimeoutTimerId_ = null; /** @private */ this.bound_ = { reconnect: this.reconnect_.bind(this), reconnectAsync: this.reconnectAsync_.bind(this), startReconnectTimeout: this.startReconnectTimeout_.bind(this), stateChanged: this.stateChanged_.bind(this), videoChannelStateChanged: this.videoChannelStateChanged_.bind(this) }; clientSession.addEventListener( remoting.ClientSession.Events.stateChanged, this.bound_.stateChanged); clientSession.addEventListener( remoting.ClientSession.Events.videoChannelStateChanged, this.bound_.videoChannelStateChanged); }; // The online event only means the network adapter is enabled, but // it doesn't necessarily mean that we have a working internet connection. // Therefore, delay the connection by |kReconnectDelay| to allow for the network // to connect. remoting.SmartReconnector.kReconnectDelay = 2000; // If no frames are received from the server for more than |kConnectionTimeout|, // disconnect the session. remoting.SmartReconnector.kConnectionTimeout = 10000; remoting.SmartReconnector.prototype = { reconnect_: function() { this.cancelPending_(); remoting.disconnect(); remoting.setMode(remoting.AppMode.CLIENT_CONNECTING); this.connector_.reconnect(); }, reconnectAsync_: function() { this.cancelPending_(); remoting.setMode(remoting.AppMode.CLIENT_CONNECTING); this.reconnectTimerId_ = window.setTimeout( this.bound_.reconnect, remoting.SmartReconnector.kReconnectDelay); }, /** * @param {remoting.ClientSession.StateEvent} event */ stateChanged_: function(event) { var State = remoting.ClientSession.State; if (event.previous === State.CONNECTED && event.current === State.FAILED) { this.cancelPending_(); if (navigator.onLine) { this.reconnect_(); } else { window.addEventListener('online', this.bound_.reconnectAsync, false); } } }, /** * @param {boolean} active This function is called if no frames are received * on the client for more than 1 second. */ videoChannelStateChanged_: function (active) { this.cancelPending_(); if (!active) { // If the channel becomes inactive due to a lack of network connection, // wait for it to go online. The plugin will try to reconnect the video // channel once it is online. If the video channels doesn't finish // reconnecting within the timeout, tear down the session and reconnect. if (navigator.onLine) { this.reconnect_(); } else { window.addEventListener( 'online', this.bound_.startReconnectTimeout, false); } } }, startReconnectTimeout_: function () { this.cancelPending_(); this.connectionTimeoutTimerId_ = window.setTimeout( this.bound_.reconnect, remoting.SmartReconnector.kConnectionTimeout); }, cancelPending_: function() { window.removeEventListener( 'online', this.bound_.startReconnectTimeout, false); window.removeEventListener('online', this.bound_.reconnectAsync, false); window.clearTimeout(this.reconnectTimerId_); window.clearTimeout(this.connectionTimeoutTimerId_); this.reconnectTimerId_ = null; this.connectionTimeoutTimerId_ = null; }, dispose: function() { this.clientSession_.removeEventListener( remoting.ClientSession.Events.stateChanged, this.bound_.stateChanged); this.clientSession_.removeEventListener( remoting.ClientSession.Events.videoChannelStateChanged, this.bound_.videoChannelStateChanged); } };