1// Copyright 2014 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.
4
5/**
6 * @fileoverview
7 * Class handling reconnecting the session when it is disconnected due to
8 * network failure.
9 *
10 * The SmartReconnector listens for changes in connection state of
11 * |clientSession| to determine if a reconnection is needed.  It then calls into
12 * |connector| to reconnect the session.
13 */
14
15'use strict';
16
17/** @suppress {duplicate} */
18var remoting = remoting || {};
19
20/**
21 * @constructor
22 * @param {remoting.SessionConnector} connector This is used to reconnect the
23 *    the session when necessary
24 * @param {remoting.ClientSession} clientSession This represents the current
25 *    remote desktop connection.  It is used to monitor the changes in
26 *    connection state.
27 * @implements {base.Disposable}
28 */
29remoting.SmartReconnector = function(connector, clientSession) {
30  /** @private */
31  this.connector_ = connector;
32
33  /** @private */
34  this.clientSession_ = clientSession;
35
36  /** @private */
37  this.reconnectTimerId_ = null;
38
39  /** @private */
40  this.connectionTimeoutTimerId_ = null;
41
42  /** @private */
43  this.bound_ = {
44    reconnect: this.reconnect_.bind(this),
45    reconnectAsync: this.reconnectAsync_.bind(this),
46    startReconnectTimeout: this.startReconnectTimeout_.bind(this),
47    stateChanged: this.stateChanged_.bind(this),
48    videoChannelStateChanged: this.videoChannelStateChanged_.bind(this)
49  };
50
51  clientSession.addEventListener(
52      remoting.ClientSession.Events.stateChanged,
53      this.bound_.stateChanged);
54  clientSession.addEventListener(
55      remoting.ClientSession.Events.videoChannelStateChanged,
56      this.bound_.videoChannelStateChanged);
57};
58
59// The online event only means the network adapter is enabled, but
60// it doesn't necessarily mean that we have a working internet connection.
61// Therefore, delay the connection by |kReconnectDelay| to allow for the network
62// to connect.
63remoting.SmartReconnector.kReconnectDelay = 2000;
64
65// If the video channel is inactive for 10 seconds reconnect the session.
66remoting.SmartReconnector.kConnectionTimeout = 10000;
67
68remoting.SmartReconnector.prototype = {
69  reconnect_: function() {
70    this.cancelPending_();
71    remoting.disconnect();
72    remoting.setMode(remoting.AppMode.CLIENT_CONNECTING);
73    this.connector_.reconnect();
74  },
75
76  reconnectAsync_: function() {
77    this.cancelPending_();
78    remoting.setMode(remoting.AppMode.CLIENT_CONNECTING);
79    this.reconnectTimerId_ = window.setTimeout(
80        this.bound_.reconnect, remoting.SmartReconnector.kReconnectDelay);
81  },
82
83  /**
84   * @param {remoting.ClientSession.StateEvent} event
85   */
86  stateChanged_: function(event) {
87    var State = remoting.ClientSession.State;
88    if (event.previous === State.CONNECTED && event.current === State.FAILED) {
89      this.cancelPending_();
90      if (navigator.onLine) {
91        this.reconnect_();
92      } else {
93        window.addEventListener('online', this.bound_.reconnectAsync, false);
94      }
95    }
96  },
97
98  /**
99   * @param {boolean} active  True if the video channel is active.
100   */
101  videoChannelStateChanged_: function (active) {
102    this.cancelPending_();
103    if (!active) {
104      window.addEventListener(
105          'online', this.bound_.startReconnectTimeout, false);
106    }
107  },
108
109  startReconnectTimeout_: function () {
110    this.cancelPending_();
111    this.connectionTimeoutTimerId_ = window.setTimeout(
112          this.bound_.reconnect, remoting.SmartReconnector.kConnectionTimeout);
113  },
114
115  cancelPending_: function() {
116    window.removeEventListener(
117        'online', this.bound_.startReconnectTimeout, false);
118    window.removeEventListener('online', this.bound_.reconnectAsync, false);
119    window.clearTimeout(this.reconnectTimerId_);
120    window.clearTimeout(this.connectionTimeoutTimerId_);
121    this.reconnectTimerId_ = null;
122    this.connectionTimeoutTimerId_ = null;
123  },
124
125  dispose: function() {
126    this.clientSession_.removeEventListener(
127        remoting.ClientSession.Events.stateChanged,
128        this.bound_.stateChanged);
129    this.clientSession_.removeEventListener(
130        remoting.ClientSession.Events.videoChannelStateChanged,
131        this.bound_.videoChannelStateChanged);
132  }
133};
134