Chromium Code Reviews| OLD | NEW |
|---|---|
| 1 // Copyright 2013 The Chromium Authors. All rights reserved. | 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 | 2 // Use of this source code is governed by a BSD-style license that can be |
| 3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
| 4 | 4 |
| 5 /** | 5 /** |
| 6 * @fileoverview | 6 * @fileoverview |
| 7 * Connect set-up state machine for Me2Me and IT2Me | 7 * Interface abstracting the SessionConnector functionality. |
|
Sergey Ulanov
2014/09/20 00:35:25
Now that the implementation is called SessionConne
Jamie
2014/09/20 00:54:22
Done.
| |
| 8 */ | 8 */ |
| 9 | 9 |
| 10 'use strict'; | 10 'use strict'; |
| 11 | 11 |
| 12 /** @suppress {duplicate} */ | 12 /** @suppress {duplicate} */ |
| 13 var remoting = remoting || {}; | 13 var remoting = remoting || {}; |
| 14 | 14 |
| 15 /** | 15 /** |
| 16 * @param {HTMLElement} clientContainer Container element for the client view. | 16 * @interface |
| 17 * @param {function(remoting.ClientSession):void} onConnected Callback on | |
| 18 * success. | |
| 19 * @param {function(remoting.Error):void} onError Callback on error. | |
| 20 * @param {function(string, string):boolean} onExtensionMessage The handler for | |
| 21 * protocol extension messages. Returns true if a message is recognized; | |
| 22 * false otherwise. | |
| 23 * @constructor | |
| 24 */ | 17 */ |
| 25 remoting.SessionConnector = function(clientContainer, onConnected, onError, | 18 remoting.SessionConnector = function() {}; |
| 26 onExtensionMessage) { | |
| 27 /** | |
| 28 * @type {HTMLElement} | |
| 29 * @private | |
| 30 */ | |
| 31 this.clientContainer_ = clientContainer; | |
| 32 | |
| 33 /** | |
| 34 * @type {function(remoting.ClientSession):void} | |
| 35 * @private | |
| 36 */ | |
| 37 this.onConnected_ = onConnected; | |
| 38 | |
| 39 /** | |
| 40 * @type {function(remoting.Error):void} | |
| 41 * @private | |
| 42 */ | |
| 43 this.onError_ = onError; | |
| 44 | |
| 45 /** | |
| 46 * @type {function(string, string):boolean} | |
| 47 * @private | |
| 48 */ | |
| 49 this.onExtensionMessage_ = onExtensionMessage; | |
| 50 | |
| 51 /** | |
| 52 * @type {string} | |
| 53 * @private | |
| 54 */ | |
| 55 this.clientJid_ = ''; | |
| 56 | |
| 57 /** | |
| 58 * @type {remoting.ClientSession.Mode} | |
| 59 * @private | |
| 60 */ | |
| 61 this.connectionMode_ = remoting.ClientSession.Mode.ME2ME; | |
| 62 | |
| 63 /** | |
| 64 * @type {remoting.SignalStrategy} | |
| 65 * @private | |
| 66 */ | |
| 67 this.signalStrategy_ = null; | |
| 68 | |
| 69 /** | |
| 70 * @type {remoting.SmartReconnector} | |
| 71 * @private | |
| 72 */ | |
| 73 this.reconnector_ = null; | |
| 74 | |
| 75 /** | |
| 76 * @private | |
| 77 */ | |
| 78 this.bound_ = { | |
| 79 onStateChange : this.onStateChange_.bind(this) | |
| 80 }; | |
| 81 | |
| 82 // Initialize/declare per-connection state. | |
| 83 this.reset(); | |
| 84 }; | |
| 85 | 19 |
| 86 /** | 20 /** |
| 87 * Reset the per-connection state so that the object can be re-used for a | 21 * Reset the per-connection state so that the object can be re-used for a |
| 88 * second connection. Note the none of the shared WCS state is reset. | 22 * second connection. Note the none of the shared WCS state is reset. |
| 89 */ | 23 */ |
| 90 remoting.SessionConnector.prototype.reset = function() { | 24 remoting.SessionConnector.prototype.reset = function() {}; |
| 91 /** | |
| 92 * Set to true to indicate that the user requested pairing when entering | |
| 93 * their PIN for a Me2Me connection. | |
| 94 * | |
| 95 * @type {boolean} | |
| 96 */ | |
| 97 this.pairingRequested = false; | |
| 98 | |
| 99 /** | |
| 100 * String used to identify the host to which to connect. For IT2Me, this is | |
| 101 * the first 7 digits of the access code; for Me2Me it is the host identifier. | |
| 102 * | |
| 103 * @type {string} | |
| 104 * @private | |
| 105 */ | |
| 106 this.hostId_ = ''; | |
| 107 | |
| 108 /** | |
| 109 * For paired connections, the client id of this device, issued by the host. | |
| 110 * | |
| 111 * @type {string} | |
| 112 * @private | |
| 113 */ | |
| 114 this.clientPairingId_ = ''; | |
| 115 | |
| 116 /** | |
| 117 * For paired connections, the paired secret for this device, issued by the | |
| 118 * host. | |
| 119 * | |
| 120 * @type {string} | |
| 121 * @private | |
| 122 */ | |
| 123 this.clientPairedSecret_ = ''; | |
| 124 | |
| 125 /** | |
| 126 * String used to authenticate to the host on connection. For IT2Me, this is | |
| 127 * the access code; for Me2Me it is the PIN. | |
| 128 * | |
| 129 * @type {string} | |
| 130 * @private | |
| 131 */ | |
| 132 this.passPhrase_ = ''; | |
| 133 | |
| 134 /** | |
| 135 * @type {string} | |
| 136 * @private | |
| 137 */ | |
| 138 this.hostJid_ = ''; | |
| 139 | |
| 140 /** | |
| 141 * @type {string} | |
| 142 * @private | |
| 143 */ | |
| 144 this.hostPublicKey_ = ''; | |
| 145 | |
| 146 /** | |
| 147 * @type {boolean} | |
| 148 * @private | |
| 149 */ | |
| 150 this.refreshHostJidIfOffline_ = false; | |
| 151 | |
| 152 /** | |
| 153 * @type {remoting.ClientSession} | |
| 154 * @private | |
| 155 */ | |
| 156 this.clientSession_ = null; | |
| 157 | |
| 158 /** | |
| 159 * @type {XMLHttpRequest} | |
| 160 * @private | |
| 161 */ | |
| 162 this.pendingXhr_ = null; | |
| 163 | |
| 164 /** | |
| 165 * Function to interactively obtain the PIN from the user. | |
| 166 * @type {function(boolean, function(string):void):void} | |
| 167 * @private | |
| 168 */ | |
| 169 this.fetchPin_ = function(onPinFetched) {}; | |
| 170 | |
| 171 /** | |
| 172 * @type {function(string, string, string, | |
| 173 * function(string, string):void): void} | |
| 174 * @private | |
| 175 */ | |
| 176 this.fetchThirdPartyToken_ = function( | |
| 177 tokenUrl, scope, onThirdPartyTokenFetched) {}; | |
| 178 | |
| 179 /** | |
| 180 * Host 'name', as displayed in the client tool-bar. For a Me2Me connection, | |
| 181 * this is the name of the host; for an IT2Me connection, it is the email | |
| 182 * address of the person sharing their computer. | |
| 183 * | |
| 184 * @type {string} | |
| 185 * @private | |
| 186 */ | |
| 187 this.hostDisplayName_ = ''; | |
| 188 }; | |
| 189 | 25 |
| 190 /** | 26 /** |
| 191 * Initiate a Me2Me connection. | 27 * Initiate a Me2Me connection. |
| 192 * | 28 * |
| 193 * @param {remoting.Host} host The Me2Me host to which to connect. | 29 * @param {remoting.Host} host The Me2Me host to which to connect. |
| 194 * @param {function(boolean, function(string):void):void} fetchPin Function to | 30 * @param {function(boolean, function(string):void):void} fetchPin Function to |
| 195 * interactively obtain the PIN from the user. | 31 * interactively obtain the PIN from the user. |
| 196 * @param {function(string, string, string, | 32 * @param {function(string, string, string, |
| 197 * function(string, string): void): void} | 33 * function(string, string): void): void} |
| 198 * fetchThirdPartyToken Function to obtain a token from a third party | 34 * fetchThirdPartyToken Function to obtain a token from a third party |
| 199 * authenticaiton server. | 35 * authenticaiton server. |
| 200 * @param {string} clientPairingId The client id issued by the host when | 36 * @param {string} clientPairingId The client id issued by the host when |
| 201 * this device was paired, if it is already paired. | 37 * this device was paired, if it is already paired. |
| 202 * @param {string} clientPairedSecret The shared secret issued by the host when | 38 * @param {string} clientPairedSecret The shared secret issued by the host when |
| 203 * this device was paired, if it is already paired. | 39 * this device was paired, if it is already paired. |
| 204 * @return {void} Nothing. | 40 * @return {void} Nothing. |
| 205 */ | 41 */ |
| 206 remoting.SessionConnector.prototype.connectMe2Me = | 42 remoting.SessionConnector.prototype.connectMe2Me = |
| 207 function(host, fetchPin, fetchThirdPartyToken, | 43 function(host, fetchPin, fetchThirdPartyToken, |
| 208 clientPairingId, clientPairedSecret) { | 44 clientPairingId, clientPairedSecret) {}; |
| 209 this.connectMe2MeInternal_( | |
| 210 host.hostId, host.jabberId, host.publicKey, host.hostName, | |
| 211 fetchPin, fetchThirdPartyToken, | |
| 212 clientPairingId, clientPairedSecret, true); | |
| 213 }; | |
| 214 | 45 |
| 215 /** | 46 /** |
| 216 * Update the pairing info so that the reconnect function will work correctly. | 47 * Update the pairing info so that the reconnect function will work correctly. |
| 217 * | 48 * |
| 218 * @param {string} clientId The paired client id. | 49 * @param {string} clientId The paired client id. |
| 219 * @param {string} sharedSecret The shared secret. | 50 * @param {string} sharedSecret The shared secret. |
| 220 */ | 51 */ |
| 221 remoting.SessionConnector.prototype.updatePairingInfo = | 52 remoting.SessionConnector.prototype.updatePairingInfo = |
| 222 function(clientId, sharedSecret) { | 53 function(clientId, sharedSecret) {}; |
| 223 this.clientPairingId_ = clientId; | |
| 224 this.clientPairedSecret_ = sharedSecret; | |
| 225 }; | |
| 226 | |
| 227 /** | |
| 228 * Initiate a Me2Me connection. | |
| 229 * | |
| 230 * @param {string} hostId ID of the Me2Me host. | |
| 231 * @param {string} hostJid XMPP JID of the host. | |
| 232 * @param {string} hostPublicKey Public Key of the host. | |
| 233 * @param {string} hostDisplayName Display name (friendly name) of the host. | |
| 234 * @param {function(boolean, function(string):void):void} fetchPin Function to | |
| 235 * interactively obtain the PIN from the user. | |
| 236 * @param {function(string, string, string, | |
| 237 * function(string, string): void): void} | |
| 238 * fetchThirdPartyToken Function to obtain a token from a third party | |
| 239 * authenticaiton server. | |
| 240 * @param {string} clientPairingId The client id issued by the host when | |
| 241 * this device was paired, if it is already paired. | |
| 242 * @param {string} clientPairedSecret The shared secret issued by the host when | |
| 243 * this device was paired, if it is already paired. | |
| 244 * @param {boolean} refreshHostJidIfOffline Whether to refresh the JID and retry | |
| 245 * the connection if the current JID is offline. | |
| 246 * @return {void} Nothing. | |
| 247 * @private | |
| 248 */ | |
| 249 remoting.SessionConnector.prototype.connectMe2MeInternal_ = | |
| 250 function(hostId, hostJid, hostPublicKey, hostDisplayName, | |
| 251 fetchPin, fetchThirdPartyToken, | |
| 252 clientPairingId, clientPairedSecret, | |
| 253 refreshHostJidIfOffline) { | |
| 254 // Cancel any existing connect operation. | |
| 255 this.cancel(); | |
| 256 | |
| 257 this.hostId_ = hostId; | |
| 258 this.hostJid_ = hostJid; | |
| 259 this.hostPublicKey_ = hostPublicKey; | |
| 260 this.fetchPin_ = fetchPin; | |
| 261 this.fetchThirdPartyToken_ = fetchThirdPartyToken; | |
| 262 this.hostDisplayName_ = hostDisplayName; | |
| 263 this.connectionMode_ = remoting.ClientSession.Mode.ME2ME; | |
| 264 this.refreshHostJidIfOffline_ = refreshHostJidIfOffline; | |
| 265 this.updatePairingInfo(clientPairingId, clientPairedSecret); | |
| 266 | |
| 267 this.connectSignaling_(); | |
| 268 } | |
| 269 | 54 |
| 270 /** | 55 /** |
| 271 * Initiate an IT2Me connection. | 56 * Initiate an IT2Me connection. |
| 272 * | 57 * |
| 273 * @param {string} accessCode The access code as entered by the user. | 58 * @param {string} accessCode The access code as entered by the user. |
| 274 * @return {void} Nothing. | 59 * @return {void} Nothing. |
| 275 */ | 60 */ |
| 276 remoting.SessionConnector.prototype.connectIT2Me = function(accessCode) { | 61 remoting.SessionConnector.prototype.connectIT2Me = |
| 277 var kSupportIdLen = 7; | 62 function(accessCode) {}; |
| 278 var kHostSecretLen = 5; | |
| 279 var kAccessCodeLen = kSupportIdLen + kHostSecretLen; | |
| 280 | |
| 281 // Cancel any existing connect operation. | |
| 282 this.cancel(); | |
| 283 | |
| 284 var normalizedAccessCode = this.normalizeAccessCode_(accessCode); | |
| 285 if (normalizedAccessCode.length != kAccessCodeLen) { | |
| 286 this.onError_(remoting.Error.INVALID_ACCESS_CODE); | |
| 287 return; | |
| 288 } | |
| 289 | |
| 290 this.hostId_ = normalizedAccessCode.substring(0, kSupportIdLen); | |
| 291 this.passPhrase_ = normalizedAccessCode; | |
| 292 this.connectionMode_ = remoting.ClientSession.Mode.IT2ME; | |
| 293 remoting.identity.callWithToken(this.connectIT2MeWithToken_.bind(this), | |
| 294 this.onError_); | |
| 295 }; | |
| 296 | 63 |
| 297 /** | 64 /** |
| 298 * Reconnect a closed connection. | 65 * Reconnect a closed connection. |
| 299 * | 66 * |
| 300 * @return {void} Nothing. | 67 * @return {void} Nothing. |
| 301 */ | 68 */ |
| 302 remoting.SessionConnector.prototype.reconnect = function() { | 69 remoting.SessionConnector.prototype.reconnect = function() {}; |
| 303 if (this.connectionMode_ == remoting.ClientSession.Mode.IT2ME) { | |
| 304 console.error('reconnect not supported for IT2Me.'); | |
| 305 return; | |
| 306 } | |
| 307 this.connectMe2MeInternal_( | |
| 308 this.hostId_, this.hostJid_, this.hostPublicKey_, this.hostDisplayName_, | |
| 309 this.fetchPin_, this.fetchThirdPartyToken_, | |
| 310 this.clientPairingId_, this.clientPairedSecret_, true); | |
| 311 }; | |
| 312 | 70 |
| 313 /** | 71 /** |
| 314 * Cancel a connection-in-progress. | 72 * Cancel a connection-in-progress. |
| 315 */ | 73 */ |
| 316 remoting.SessionConnector.prototype.cancel = function() { | 74 remoting.SessionConnector.prototype.cancel = function() {}; |
| 317 if (this.clientSession_) { | |
| 318 this.clientSession_.removePlugin(); | |
| 319 this.clientSession_ = null; | |
| 320 } | |
| 321 if (this.pendingXhr_) { | |
| 322 this.pendingXhr_.abort(); | |
| 323 this.pendingXhr_ = null; | |
| 324 } | |
| 325 this.reset(); | |
| 326 }; | |
| 327 | 75 |
| 328 /** | 76 /** |
| 329 * Get the connection mode (Me2Me or IT2Me) | 77 * Get the connection mode (Me2Me or IT2Me) |
| 330 * | 78 * |
| 331 * @return {remoting.ClientSession.Mode} | 79 * @return {remoting.ClientSession.Mode} |
| 332 */ | 80 */ |
| 333 remoting.SessionConnector.prototype.getConnectionMode = function() { | 81 remoting.SessionConnector.prototype.getConnectionMode = function() {}; |
| 334 return this.connectionMode_; | |
| 335 }; | |
| 336 | 82 |
| 337 /** | 83 /** |
| 338 * Get host ID. | 84 * Get host ID. |
| 339 * | 85 * |
| 340 * @return {string} | 86 * @return {string} |
| 341 */ | 87 */ |
| 342 remoting.SessionConnector.prototype.getHostId = function() { | 88 remoting.SessionConnector.prototype.getHostId = function() {}; |
| 343 return this.hostId_; | 89 |
| 344 }; | |
| 345 | 90 |
| 346 /** | 91 /** |
| 347 * @private | 92 * @interface |
| 348 */ | 93 */ |
| 349 remoting.SessionConnector.prototype.connectSignaling_ = function() { | 94 remoting.SessionConnectorFactory = function() {}; |
| 350 base.dispose(this.signalStrategy_); | |
| 351 this.signalStrategy_ = null; | |
| 352 | |
| 353 /** @type {remoting.SessionConnector} */ | |
| 354 var that = this; | |
| 355 | |
| 356 /** @param {string} token */ | |
| 357 function connectSignalingWithToken(token) { | |
| 358 remoting.identity.getEmail( | |
| 359 connectSignalingWithTokenAndEmail.bind(null, token), that.onError_); | |
| 360 } | |
| 361 | |
| 362 /** | |
| 363 * @param {string} token | |
| 364 * @param {string} email | |
| 365 */ | |
| 366 function connectSignalingWithTokenAndEmail(token, email) { | |
| 367 that.signalStrategy_.connect( | |
| 368 remoting.settings.XMPP_SERVER_ADDRESS, email, token); | |
| 369 } | |
| 370 | |
| 371 // Only use XMPP when TCP API is available and TLS support is enabled. That's | |
| 372 // not the case for V1 app (socket API is available only to platform apps) | |
| 373 // and for Chrome releases before 38. | |
| 374 if (chrome.socket && chrome.socket.secure) { | |
| 375 this.signalStrategy_ = /** @type {remoting.SignalStrategy} */ | |
| 376 (new remoting.XmppConnection(this.onSignalingState_.bind(this))); | |
| 377 } else { | |
| 378 this.signalStrategy_ = /** @type {remoting.SignalStrategy} */ | |
| 379 (new remoting.WcsAdapter(this.onSignalingState_.bind(this))); | |
| 380 } | |
| 381 | |
| 382 remoting.identity.callWithToken(connectSignalingWithToken, this.onError_); | |
| 383 }; | |
| 384 | 95 |
| 385 /** | 96 /** |
| 386 * @private | 97 * @param {HTMLElement} clientContainer Container element for the client view. |
| 387 * @param {remoting.SignalStrategy.State} state | 98 * @param {function(remoting.ClientSession):void} onConnected Callback on |
| 99 * success. | |
| 100 * @param {function(remoting.Error):void} onError Callback on error. | |
| 101 * @param {function(string, string):boolean} onExtensionMessage The handler for | |
| 102 * protocol extension messages. Returns true if a message is recognized; | |
| 103 * false otherwise. | |
| 104 * @return {remoting.SessionConnector} | |
| 388 */ | 105 */ |
| 389 remoting.SessionConnector.prototype.onSignalingState_ = function(state) { | 106 remoting.SessionConnectorFactory.prototype.createConnector = |
| 390 switch (state) { | 107 function(clientContainer, onConnected, onError, onExtensionMessage) {}; |
| 391 case remoting.SignalStrategy.State.CONNECTED: | |
| 392 // Proceed only if the connection hasn't been canceled. | |
| 393 if (this.hostJid_) { | |
| 394 this.createSession_(); | |
| 395 } | |
| 396 break; | |
| 397 | |
| 398 case remoting.SignalStrategy.State.FAILED: | |
| 399 this.onError_(this.signalStrategy_.getError()); | |
| 400 break; | |
| 401 } | |
| 402 }; | |
| 403 | 108 |
| 404 /** | 109 /** |
| 405 * Continue an IT2Me connection once an access token has been obtained. | 110 * @type {remoting.SessionConnectorFactory} |
| 406 * | |
| 407 * @param {string} token An OAuth2 access token. | |
| 408 * @return {void} Nothing. | |
| 409 * @private | |
| 410 */ | 111 */ |
| 411 remoting.SessionConnector.prototype.connectIT2MeWithToken_ = function(token) { | 112 remoting.SessionConnector.factory = null; |
| 412 // Resolve the host id to get the host JID. | |
| 413 this.pendingXhr_ = remoting.xhr.get( | |
| 414 remoting.settings.DIRECTORY_API_BASE_URL + '/support-hosts/' + | |
| 415 encodeURIComponent(this.hostId_), | |
| 416 this.onIT2MeHostInfo_.bind(this), | |
| 417 '', | |
| 418 { 'Authorization': 'OAuth ' + token }); | |
| 419 }; | |
| 420 | |
| 421 /** | |
| 422 * Continue an IT2Me connection once the host JID has been looked up. | |
| 423 * | |
| 424 * @param {XMLHttpRequest} xhr The server response to the support-hosts query. | |
| 425 * @return {void} Nothing. | |
| 426 * @private | |
| 427 */ | |
| 428 remoting.SessionConnector.prototype.onIT2MeHostInfo_ = function(xhr) { | |
| 429 this.pendingXhr_ = null; | |
| 430 if (xhr.status == 200) { | |
| 431 var host = /** @type {{data: {jabberId: string, publicKey: string}}} */ | |
| 432 jsonParseSafe(xhr.responseText); | |
| 433 if (host && host.data && host.data.jabberId && host.data.publicKey) { | |
| 434 this.hostJid_ = host.data.jabberId; | |
| 435 this.hostPublicKey_ = host.data.publicKey; | |
| 436 this.hostDisplayName_ = this.hostJid_.split('/')[0]; | |
| 437 this.createSession_(); | |
| 438 return; | |
| 439 } else { | |
| 440 console.error('Invalid "support-hosts" response from server.'); | |
| 441 } | |
| 442 } else { | |
| 443 this.onError_(this.translateSupportHostsError(xhr.status)); | |
| 444 } | |
| 445 }; | |
| 446 | |
| 447 /** | |
| 448 * Creates ClientSession object. | |
| 449 */ | |
| 450 remoting.SessionConnector.prototype.createSession_ = function() { | |
| 451 // In some circumstances, the WCS <iframe> can get reloaded, which results | |
| 452 // in a new clientJid and a new callback. In this case, remove the old | |
| 453 // client plugin before instantiating a new one. | |
| 454 if (this.clientSession_) { | |
| 455 this.clientSession_.removePlugin(); | |
| 456 this.clientSession_ = null; | |
| 457 } | |
| 458 | |
| 459 var authenticationMethods = | |
| 460 'third_party,spake2_pair,spake2_hmac,spake2_plain'; | |
| 461 this.clientSession_ = new remoting.ClientSession( | |
| 462 this.signalStrategy_, this.clientContainer_, this.hostDisplayName_, | |
| 463 this.passPhrase_, this.fetchPin_, this.fetchThirdPartyToken_, | |
| 464 authenticationMethods, this.hostId_, this.hostJid_, this.hostPublicKey_, | |
| 465 this.connectionMode_, this.clientPairingId_, this.clientPairedSecret_); | |
| 466 this.clientSession_.logHostOfflineErrors(!this.refreshHostJidIfOffline_); | |
| 467 this.clientSession_.addEventListener( | |
| 468 remoting.ClientSession.Events.stateChanged, | |
| 469 this.bound_.onStateChange); | |
| 470 this.clientSession_.createPluginAndConnect(this.onExtensionMessage_); | |
| 471 }; | |
| 472 | |
| 473 /** | |
| 474 * Handle a change in the state of the client session prior to successful | |
| 475 * connection (after connection, this class no longer handles state change | |
| 476 * events). Errors that occur while connecting either trigger a reconnect | |
| 477 * or notify the onError handler. | |
| 478 * | |
| 479 * @param {remoting.ClientSession.StateEvent} event | |
| 480 * @return {void} Nothing. | |
| 481 * @private | |
| 482 */ | |
| 483 remoting.SessionConnector.prototype.onStateChange_ = function(event) { | |
| 484 switch (event.current) { | |
| 485 case remoting.ClientSession.State.CONNECTED: | |
| 486 // When the connection succeeds, deregister for state-change callbacks | |
| 487 // and pass the session to the onConnected callback. It is expected that | |
| 488 // it will register a new state-change callback to handle disconnect | |
| 489 // or error conditions. | |
| 490 this.clientSession_.removeEventListener( | |
| 491 remoting.ClientSession.Events.stateChanged, | |
| 492 this.bound_.onStateChange); | |
| 493 | |
| 494 base.dispose(this.reconnector_); | |
| 495 if (this.connectionMode_ != remoting.ClientSession.Mode.IT2ME) { | |
| 496 this.reconnector_ = | |
| 497 new remoting.SmartReconnector(this, this.clientSession_); | |
| 498 } | |
| 499 this.onConnected_(this.clientSession_); | |
| 500 break; | |
| 501 | |
| 502 case remoting.ClientSession.State.CREATED: | |
| 503 console.log('Created plugin'); | |
| 504 break; | |
| 505 | |
| 506 case remoting.ClientSession.State.CONNECTING: | |
| 507 console.log('Connecting as ' + remoting.identity.getCachedEmail()); | |
| 508 break; | |
| 509 | |
| 510 case remoting.ClientSession.State.INITIALIZING: | |
| 511 console.log('Initializing connection'); | |
| 512 break; | |
| 513 | |
| 514 case remoting.ClientSession.State.CLOSED: | |
| 515 // This class deregisters for state-change callbacks when the CONNECTED | |
| 516 // state is reached, so it only sees the CLOSED state in exceptional | |
| 517 // circumstances. For example, a CONNECTING -> CLOSED transition happens | |
| 518 // if the host closes the connection without an error message instead of | |
| 519 // accepting it. Since there's no way of knowing exactly what went wrong, | |
| 520 // we rely on server-side logs in this case and report a generic error | |
| 521 // message. | |
| 522 this.onError_(remoting.Error.UNEXPECTED); | |
| 523 break; | |
| 524 | |
| 525 case remoting.ClientSession.State.FAILED: | |
| 526 var error = this.clientSession_.getError(); | |
| 527 console.error('Client plugin reported connection failed: ' + error); | |
| 528 if (error == null) { | |
| 529 error = remoting.Error.UNEXPECTED; | |
| 530 } | |
| 531 if (error == remoting.Error.HOST_IS_OFFLINE && | |
| 532 this.refreshHostJidIfOffline_) { | |
| 533 // The plugin will be re-created when the host finished refreshing | |
| 534 remoting.hostList.refresh(this.onHostListRefresh_.bind(this)); | |
| 535 } else { | |
| 536 this.onError_(error); | |
| 537 } | |
| 538 break; | |
| 539 | |
| 540 default: | |
| 541 console.error('Unexpected client plugin state: ' + event.current); | |
| 542 // This should only happen if the web-app and client plugin get out of | |
| 543 // sync, and even then the version check should ensure compatibility. | |
| 544 this.onError_(remoting.Error.MISSING_PLUGIN); | |
| 545 } | |
| 546 }; | |
| 547 | |
| 548 /** | |
| 549 * @param {boolean} success True if the host list was successfully refreshed; | |
| 550 * false if an error occurred. | |
| 551 * @private | |
| 552 */ | |
| 553 remoting.SessionConnector.prototype.onHostListRefresh_ = function(success) { | |
| 554 if (success) { | |
| 555 var host = remoting.hostList.getHostForId(this.hostId_); | |
| 556 if (host) { | |
| 557 this.connectMe2MeInternal_( | |
| 558 host.hostId, host.jabberId, host.publicKey, host.hostName, | |
| 559 this.fetchPin_, this.fetchThirdPartyToken_, | |
| 560 this.clientPairingId_, this.clientPairedSecret_, false); | |
| 561 return; | |
| 562 } | |
| 563 } | |
| 564 this.onError_(remoting.Error.HOST_IS_OFFLINE); | |
| 565 }; | |
| 566 | |
| 567 /** | |
| 568 * @param {number} error An HTTP error code returned by the support-hosts | |
| 569 * endpoint. | |
| 570 * @return {remoting.Error} The equivalent remoting.Error code. | |
| 571 * @private | |
| 572 */ | |
| 573 remoting.SessionConnector.prototype.translateSupportHostsError = | |
| 574 function(error) { | |
| 575 switch (error) { | |
| 576 case 0: return remoting.Error.NETWORK_FAILURE; | |
| 577 case 404: return remoting.Error.INVALID_ACCESS_CODE; | |
| 578 case 502: // No break | |
| 579 case 503: return remoting.Error.SERVICE_UNAVAILABLE; | |
| 580 default: return remoting.Error.UNEXPECTED; | |
| 581 } | |
| 582 }; | |
| 583 | |
| 584 /** | |
| 585 * Normalize the access code entered by the user. | |
| 586 * | |
| 587 * @param {string} accessCode The access code, as entered by the user. | |
| 588 * @return {string} The normalized form of the code (whitespace removed). | |
| 589 */ | |
| 590 remoting.SessionConnector.prototype.normalizeAccessCode_ = | |
| 591 function(accessCode) { | |
| 592 // Trim whitespace. | |
| 593 return accessCode.replace(/\s/g, ''); | |
| 594 }; | |
| OLD | NEW |