Chromium Code Reviews| OLD | NEW |
|---|---|
| (Empty) | |
| 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 'use strict'; | |
| 6 | |
| 7 /** @suppress {duplicate} */ | |
| 8 var remoting = remoting || {}; | |
| 9 | |
| 10 /** | |
| 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|. | |
| 14 * | |
| 15 * @param {function( | |
| 16 * function(remoting.SignalStrategy.State) | |
| 17 * ):remoting.SignalStrategy} primaryFactory | |
| 18 * @param {function( | |
| 19 * function(remoting.SignalStrategy.State) | |
| 20 * ):remoting.SignalStrategy} secondaryFactory | |
| 21 * @param {function(remoting.SignalStrategy.State):void} onStateChangedCallback | |
| 22 * @param {function(remoting.FallbackSignalStrategy.Result)} onProgressCallback | |
| 23 * | |
| 24 * @implements {remoting.SignalStrategy} | |
| 25 * @constructor | |
| 26 */ | |
| 27 remoting.FallbackSignalStrategy = function( | |
| 28 primaryFactory, secondaryFactory, | |
| 29 onStateChangedCallback, onProgressCallback) { | |
| 30 /** | |
| 31 * @type {remoting.SignalStrategy} | |
| 32 * @private | |
| 33 */ | |
| 34 this.primary_ = primaryFactory(this.onStateChanged_.bind(this, true)); | |
| 35 | |
| 36 /** | |
| 37 * @type {remoting.SignalStrategy} | |
| 38 * @private | |
| 39 */ | |
| 40 this.secondary_ = secondaryFactory(this.onStateChanged_.bind(this, false)); | |
| 41 | |
| 42 /** | |
| 43 * @type {function(remoting.SignalStrategy.State)} | |
| 44 * @private | |
| 45 */ | |
| 46 this.onStateChangedCallback_ = onStateChangedCallback; | |
| 47 | |
| 48 /** | |
| 49 * @type {function(remoting.FallbackSignalStrategy.Result)} | |
| 50 * @private | |
| 51 */ | |
| 52 this.onProgressCallback_ = onProgressCallback; | |
| 53 | |
| 54 /** | |
| 55 * @type {?function(Element):void} | |
| 56 * @private | |
| 57 */ | |
| 58 this.onIncomingStanzaCallback_ = null; | |
| 59 | |
| 60 /** | |
| 61 * @type {number} | |
| 62 * @private | |
| 63 * @const | |
| 64 */ | |
| 65 this.PRIMARY_CONNECT_TIMEOUT_MS_ = 10 * 1000; | |
| 66 | |
| 67 /** | |
| 68 * @enum {string} | |
| 69 * @private | |
| 70 */ | |
| 71 this.State = { | |
|
kelvinp
2015/01/13 19:22:47
Enum should be defined on the 'class' remoting.Fal
Jamie
2015/01/14 01:36:26
I considered that, but it ends up being incredibly
| |
| 72 NOT_CONNECTED: 'not-connected', | |
| 73 PRIMARY_PENDING: 'primary-pending', | |
| 74 PRIMARY_SUCCEEDED: 'primary-succeeded', | |
| 75 SECONDARY_PENDING: 'secondary-pending', | |
| 76 SECONDARY_SUCCEEDED: 'secondary-succeeded', | |
| 77 SECONDARY_FAILED: 'secondary-failed', | |
| 78 CLOSED: 'closed' | |
| 79 }; | |
| 80 | |
| 81 /** | |
| 82 * @type {string} | |
| 83 * @private | |
| 84 */ | |
| 85 this.state_ = this.State.NOT_CONNECTED; | |
| 86 | |
| 87 /** | |
| 88 * @type {?remoting.SignalStrategy.State} | |
| 89 * @private | |
| 90 */ | |
| 91 this.externalState_ = null; | |
| 92 | |
| 93 /** | |
| 94 * @type {string} | |
| 95 * @private | |
| 96 */ | |
| 97 this.server_ = ''; | |
| 98 | |
| 99 /** | |
| 100 * @type {string} | |
| 101 * @private | |
| 102 */ | |
| 103 this.username_ = ''; | |
| 104 | |
| 105 /** | |
| 106 * @type {string} | |
| 107 * @private | |
| 108 */ | |
| 109 this.authToken_ = ''; | |
| 110 | |
| 111 /** | |
| 112 * @type {number} | |
| 113 * @private | |
| 114 */ | |
| 115 this.primaryConnectTimerId_ = 0; | |
| 116 }; | |
| 117 | |
| 118 /** | |
| 119 * @enum {string} | |
| 120 */ | |
| 121 remoting.FallbackSignalStrategy.Result = { | |
|
kelvinp
2015/01/13 19:22:47
Nit: ProgressResult. To make it clear that this i
Jamie
2015/01/14 01:36:26
I changed it to Progress, since it's not always a
kelvinp
2015/01/14 19:46:22
Done.
| |
| 122 PRIMARY_SUCCEEDED: 'primary-succeeded', | |
| 123 PRIMARY_FAILED: 'primary-failed', | |
| 124 PRIMARY_TIMED_OUT: 'primary-timed-out', | |
| 125 PRIMARY_SUCCEEDED_LATE: 'primary-succeeded-late', | |
| 126 PRIMARY_FAILED_LATE: 'primary-failed-late', | |
| 127 SECONDARY_SUCCEEDED: 'secondary-succeeded', | |
| 128 SECONDARY_FAILED: 'secondary-failed' | |
| 129 }; | |
| 130 | |
| 131 remoting.FallbackSignalStrategy.prototype.dispose = function() { | |
| 132 this.primary_.dispose(); | |
| 133 this.secondary_.dispose(); | |
| 134 }; | |
| 135 | |
| 136 /** | |
| 137 * @param {?function(Element):void} onIncomingStanzaCallback Callback to call on | |
| 138 * incoming messages. | |
| 139 */ | |
| 140 remoting.FallbackSignalStrategy.prototype.setIncomingStanzaCallback = | |
| 141 function(onIncomingStanzaCallback) { | |
| 142 this.onIncomingStanzaCallback_ = onIncomingStanzaCallback; | |
| 143 if (this.state_ == this.State.PRIMARY_PENDING || | |
| 144 this.state_ == this.State.PRIMARY_SUCCEEDED) { | |
| 145 this.primary_.setIncomingStanzaCallback(onIncomingStanzaCallback); | |
| 146 } else if (this.state_ == this.State.SECONDARY_PENDING || | |
| 147 this.state_ == this.State.SECONDARY_SUCCEEDED) { | |
| 148 this.secondary_.setIncomingStanzaCallback(onIncomingStanzaCallback); | |
| 149 } | |
| 150 }; | |
| 151 | |
| 152 /** | |
| 153 * @param {string} server | |
| 154 * @param {string} username | |
| 155 * @param {string} authToken | |
| 156 */ | |
| 157 remoting.FallbackSignalStrategy.prototype.connect = | |
| 158 function(server, username, authToken) { | |
| 159 base.debug.assert(this.state_ == this.State.NOT_CONNECTED); | |
| 160 this.server_ = server; | |
| 161 this.username_ = username; | |
| 162 this.authToken_ = authToken; | |
| 163 this.state_ = this.State.PRIMARY_PENDING; | |
| 164 this.primary_.setIncomingStanzaCallback(this.onIncomingStanzaCallback_); | |
| 165 this.primary_.connect(server, username, authToken); | |
| 166 this.primaryConnectTimerId_ = | |
| 167 window.setTimeout(this.onPrimaryTimeout_.bind(this), | |
| 168 this.PRIMARY_CONNECT_TIMEOUT_MS_); | |
| 169 }; | |
| 170 | |
| 171 /** | |
| 172 * Sends a message. Can be called only in CONNECTED state. | |
| 173 * @param {string} message | |
| 174 */ | |
| 175 remoting.FallbackSignalStrategy.prototype.sendMessage = function(message) { | |
| 176 this.getConnectedSignalStrategy_().sendMessage(message); | |
| 177 }; | |
| 178 | |
| 179 /** @return {remoting.SignalStrategy.State} Current state */ | |
| 180 remoting.FallbackSignalStrategy.prototype.getState = function() { | |
| 181 return (this.externalState_ === null) | |
| 182 ? remoting.SignalStrategy.State.NOT_CONNECTED | |
| 183 : this.externalState_; | |
| 184 }; | |
| 185 | |
| 186 /** @return {remoting.Error} Error when in FAILED state. */ | |
| 187 remoting.FallbackSignalStrategy.prototype.getError = function() { | |
| 188 base.debug.assert(this.state_ == this.State.SECONDARY_FAILED); | |
| 189 base.debug.assert( | |
| 190 this.secondary_.getState() == remoting.SignalStrategy.State.FAILED); | |
| 191 return this.secondary_.getError(); | |
| 192 }; | |
| 193 | |
| 194 /** @return {string} Current JID when in CONNECTED state. */ | |
| 195 remoting.FallbackSignalStrategy.prototype.getJid = function() { | |
| 196 return this.getConnectedSignalStrategy_().getJid(); | |
| 197 }; | |
| 198 | |
| 199 /** | |
| 200 * @return {remoting.SignalStrategy} The active signal strategy, if the | |
| 201 * connection has succeeded. | |
| 202 * @private | |
| 203 */ | |
| 204 remoting.FallbackSignalStrategy.prototype.getConnectedSignalStrategy_ = | |
| 205 function() { | |
| 206 if (this.state_ == this.State.PRIMARY_SUCCEEDED) { | |
| 207 base.debug.assert( | |
| 208 this.primary_.getState() == remoting.SignalStrategy.State.CONNECTED); | |
| 209 return this.primary_; | |
| 210 } else if (this.state_ == this.State.SECONDARY_SUCCEEDED) { | |
| 211 base.debug.assert( | |
| 212 this.secondary_.getState() == remoting.SignalStrategy.State.CONNECTED); | |
| 213 return this.secondary_; | |
| 214 } else { | |
| 215 base.debug.assert( | |
| 216 false, | |
| 217 'getConnectedSignalStrategy called in unconnected state'); | |
| 218 return null; | |
| 219 } | |
| 220 }; | |
| 221 | |
| 222 /** | |
| 223 * @param {boolean} isPrimary | |
| 224 * @param {remoting.SignalStrategy.State} state | |
| 225 * @private | |
| 226 */ | |
| 227 remoting.FallbackSignalStrategy.prototype.onStateChanged_ = | |
|
Sergey Ulanov
2015/01/13 19:43:51
Maybe separate this into two callbacks, one for pr
Jamie
2015/01/14 01:36:26
Done.
| |
| 228 function(isPrimary, state) { | |
| 229 /** | |
| 230 * @type {boolean} True to notify the external callback of the new state | |
| 231 * (ie, if it represents a later stage of the connection process than | |
| 232 * has previously been notified). This ensures that, for example, the | |
| 233 * external callback won't see a CONNECTING -> NOT_CONNECTED transition | |
| 234 * when we switch from the primary to the secondary signal strategy. | |
| 235 */ | |
| 236 var notifyExternalCallback = (this.externalState_ === null) || | |
| 237 (state > this.externalState_); | |
| 238 | |
| 239 switch (state) { | |
| 240 case remoting.SignalStrategy.State.CONNECTED: | |
| 241 if (isPrimary) { | |
| 242 if (this.state_ == this.State.PRIMARY_PENDING) { | |
| 243 window.clearTimeout(this.primaryConnectTimerId_); | |
| 244 this.onProgressCallback_( | |
| 245 remoting.FallbackSignalStrategy.Result.PRIMARY_SUCCEEDED); | |
| 246 this.state_ = this.State.PRIMARY_SUCCEEDED; | |
| 247 } else { | |
| 248 this.onProgressCallback_( | |
| 249 remoting.FallbackSignalStrategy.Result.PRIMARY_SUCCEEDED_LATE); | |
| 250 } | |
| 251 } else { | |
| 252 this.onProgressCallback_( | |
|
kelvinp
2015/01/13 19:22:48
base.debug.assert(this.state_ == this.State.SECOND
Jamie
2015/01/14 01:36:26
I've factored this into two separate functions. LM
kelvinp
2015/01/14 19:46:22
I think the code is much clearer with two separate
| |
| 253 remoting.FallbackSignalStrategy.Result.SECONDARY_SUCCEEDED); | |
| 254 this.state_ = this.State.SECONDARY_SUCCEEDED; | |
| 255 } | |
| 256 break; | |
| 257 | |
| 258 case remoting.SignalStrategy.State.FAILED: | |
| 259 notifyExternalCallback = !isPrimary; | |
| 260 if (isPrimary) { | |
| 261 if (this.state_ == this.State.PRIMARY_PENDING) { | |
| 262 window.clearTimeout(this.primaryConnectTimerId_); | |
| 263 this.onProgressCallback_( | |
| 264 remoting.FallbackSignalStrategy.Result.PRIMARY_FAILED); | |
| 265 this.reconnect_(); | |
| 266 } else { | |
| 267 this.onProgressCallback_( | |
| 268 remoting.FallbackSignalStrategy.Result.PRIMARY_FAILED_LATE); | |
| 269 } | |
| 270 } else { | |
| 271 this.onProgressCallback_( | |
| 272 remoting.FallbackSignalStrategy.Result.SECONDARY_FAILED); | |
| 273 } | |
| 274 break; | |
| 275 | |
| 276 case remoting.SignalStrategy.State.CLOSED: | |
| 277 this.state_ = this.State.CLOSED; | |
| 278 break; | |
| 279 } | |
| 280 | |
| 281 if (notifyExternalCallback) { | |
| 282 this.externalState_ = state; | |
| 283 this.onStateChangedCallback_(state); | |
| 284 } | |
| 285 }; | |
| 286 | |
| 287 /** | |
| 288 * @private | |
| 289 */ | |
| 290 remoting.FallbackSignalStrategy.prototype.reconnect_ = function() { | |
|
Sergey Ulanov
2015/01/13 19:43:51
maybe call it connectSecondary_()?
Jamie
2015/01/14 01:36:26
Done.
| |
| 291 base.debug.assert(this.state_ == this.State.PRIMARY_PENDING); | |
| 292 base.debug.assert(this.server_ != ''); | |
| 293 base.debug.assert(this.username_ != ''); | |
| 294 base.debug.assert(this.authToken_ != ''); | |
| 295 | |
| 296 this.state_ = this.State.SECONDARY_PENDING; | |
| 297 this.primary_.setIncomingStanzaCallback(null); | |
| 298 this.secondary_.setIncomingStanzaCallback(this.onIncomingStanzaCallback_); | |
| 299 this.secondary_.connect(this.server_, this.username_, this.authToken_); | |
|
kelvinp
2015/01/13 19:22:48
Does the primary and the secondary signal strategy
Sergey Ulanov
2015/01/13 19:43:51
we use the same token for xmpp and wcs
| |
| 300 }; | |
| 301 | |
| 302 /** | |
| 303 * @private | |
| 304 */ | |
| 305 remoting.FallbackSignalStrategy.prototype.onPrimaryTimeout_ = function() { | |
| 306 this.onProgressCallback_( | |
| 307 remoting.FallbackSignalStrategy.Result.PRIMARY_TIMED_OUT); | |
| 308 this.reconnect_(); | |
| 309 }; | |
| 310 | |
| 311 /** | |
| 312 * @return {remoting.SignalStrategy} The primary signal strategy. | |
| 313 * @private | |
|
Sergey Ulanov
2015/01/13 19:43:51
This is called from tests so it isn't really priva
Jamie
2015/01/14 01:36:26
True, but marking it as private means that the com
Sergey Ulanov
2015/01/14 19:42:43
sgtm
| |
| 314 */ | |
| 315 remoting.FallbackSignalStrategy.prototype.getPrimaryStrategyForTesting_ = | |
| 316 function() { | |
| 317 return this.primary_; | |
| 318 }; | |
| 319 | |
| 320 /** | |
| 321 * @return {remoting.SignalStrategy} The primary signal strategy. | |
| 322 * @private | |
| 323 */ | |
| 324 remoting.FallbackSignalStrategy.prototype.getSecondaryStrategyForTesting_ = | |
| 325 function() { | |
| 326 return this.secondary_; | |
| 327 }; | |
| OLD | NEW |