summaryrefslogtreecommitdiff
path: root/remoting/webapp/smart_reconnector.js
blob: 55186b4205e68abd76e6d0cd58eae5af10e639ec (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
// 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);
  }
};