OLD | NEW |
1 // Copyright 2013 The Chromium Authors. All rights reserved. | 1 // Copyright 2013 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 * Connect set-up state machine for Me2Me and IT2Me |
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 * @param {HTMLElement} clientContainer Container element for the client view. |
17 * @param {function(remoting.ClientSession):void} onConnected Callback on | 17 * @param {remoting.Application} app The main remoting application. |
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 | 18 * @constructor |
24 * @implements {remoting.SessionConnector} | 19 * @implements {remoting.SessionConnector} |
25 */ | 20 */ |
26 remoting.SessionConnectorImpl = function(clientContainer, onConnected, onError, | 21 remoting.SessionConnectorImpl = function(clientContainer, app) { |
27 onExtensionMessage) { | |
28 /** | 22 /** |
29 * @type {HTMLElement} | 23 * @type {HTMLElement} |
30 * @private | 24 * @private |
31 */ | 25 */ |
32 this.clientContainer_ = clientContainer; | 26 this.clientContainer_ = clientContainer; |
33 | 27 |
34 /** | 28 /** |
35 * @type {function(remoting.ClientSession):void} | 29 * @type {remoting.Application} |
36 * @private | 30 * @private |
37 */ | 31 */ |
38 this.onConnected_ = onConnected; | 32 this.app_ = app; |
39 | |
40 /** | |
41 * @type {function(remoting.Error):void} | |
42 * @private | |
43 */ | |
44 this.onError_ = onError; | |
45 | |
46 /** | |
47 * @type {function(string, string):boolean} | |
48 * @private | |
49 */ | |
50 this.onExtensionMessage_ = onExtensionMessage; | |
51 | 33 |
52 /** | 34 /** |
53 * @type {string} | 35 * @type {string} |
54 * @private | 36 * @private |
55 */ | 37 */ |
56 this.clientJid_ = ''; | 38 this.clientJid_ = ''; |
57 | 39 |
58 /** | 40 /** |
59 * @type {remoting.ClientSession.Mode} | 41 * @type {remoting.ClientSession.Mode} |
60 * @private | 42 * @private |
(...skipping 208 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
269 remoting.SessionConnectorImpl.prototype.connectIT2Me = function(accessCode) { | 251 remoting.SessionConnectorImpl.prototype.connectIT2Me = function(accessCode) { |
270 var kSupportIdLen = 7; | 252 var kSupportIdLen = 7; |
271 var kHostSecretLen = 5; | 253 var kHostSecretLen = 5; |
272 var kAccessCodeLen = kSupportIdLen + kHostSecretLen; | 254 var kAccessCodeLen = kSupportIdLen + kHostSecretLen; |
273 | 255 |
274 // Cancel any existing connect operation. | 256 // Cancel any existing connect operation. |
275 this.cancel(); | 257 this.cancel(); |
276 | 258 |
277 var normalizedAccessCode = this.normalizeAccessCode_(accessCode); | 259 var normalizedAccessCode = this.normalizeAccessCode_(accessCode); |
278 if (normalizedAccessCode.length != kAccessCodeLen) { | 260 if (normalizedAccessCode.length != kAccessCodeLen) { |
279 this.onError_(remoting.Error.INVALID_ACCESS_CODE); | 261 this.app_.onError(remoting.Error.INVALID_ACCESS_CODE); |
280 return; | 262 return; |
281 } | 263 } |
282 | 264 |
283 this.hostId_ = normalizedAccessCode.substring(0, kSupportIdLen); | 265 this.hostId_ = normalizedAccessCode.substring(0, kSupportIdLen); |
284 this.passPhrase_ = normalizedAccessCode; | 266 this.passPhrase_ = normalizedAccessCode; |
285 this.connectionMode_ = remoting.ClientSession.Mode.IT2ME; | 267 this.connectionMode_ = remoting.ClientSession.Mode.IT2ME; |
286 remoting.identity.callWithToken(this.connectIT2MeWithToken_.bind(this), | 268 remoting.identity.callWithToken(this.connectIT2MeWithToken_.bind(this), |
287 this.onError_); | 269 this.app_.onError); |
288 }; | 270 }; |
289 | 271 |
290 /** | 272 /** |
291 * Reconnect a closed connection. | 273 * Reconnect a closed connection. |
292 * | 274 * |
293 * @return {void} Nothing. | 275 * @return {void} Nothing. |
294 */ | 276 */ |
295 remoting.SessionConnectorImpl.prototype.reconnect = function() { | 277 remoting.SessionConnectorImpl.prototype.reconnect = function() { |
296 if (this.connectionMode_ == remoting.ClientSession.Mode.IT2ME) { | 278 if (this.connectionMode_ == remoting.ClientSession.Mode.IT2ME) { |
297 console.error('reconnect not supported for IT2Me.'); | 279 console.error('reconnect not supported for IT2Me.'); |
(...skipping 44 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
342 remoting.SessionConnectorImpl.prototype.connectSignaling_ = function() { | 324 remoting.SessionConnectorImpl.prototype.connectSignaling_ = function() { |
343 base.dispose(this.signalStrategy_); | 325 base.dispose(this.signalStrategy_); |
344 this.signalStrategy_ = null; | 326 this.signalStrategy_ = null; |
345 | 327 |
346 /** @type {remoting.SessionConnectorImpl} */ | 328 /** @type {remoting.SessionConnectorImpl} */ |
347 var that = this; | 329 var that = this; |
348 | 330 |
349 /** @param {string} token */ | 331 /** @param {string} token */ |
350 function connectSignalingWithToken(token) { | 332 function connectSignalingWithToken(token) { |
351 remoting.identity.getEmail( | 333 remoting.identity.getEmail( |
352 connectSignalingWithTokenAndEmail.bind(null, token), that.onError_); | 334 connectSignalingWithTokenAndEmail.bind(null, token), that.app_.onError); |
353 } | 335 } |
354 | 336 |
355 /** | 337 /** |
356 * @param {string} token | 338 * @param {string} token |
357 * @param {string} email | 339 * @param {string} email |
358 */ | 340 */ |
359 function connectSignalingWithTokenAndEmail(token, email) { | 341 function connectSignalingWithTokenAndEmail(token, email) { |
360 that.signalStrategy_.connect( | 342 that.signalStrategy_.connect( |
361 remoting.settings.XMPP_SERVER_ADDRESS, email, token); | 343 remoting.settings.XMPP_SERVER_ADDRESS, email, token); |
362 } | 344 } |
363 | 345 |
364 this.signalStrategy_ = | 346 this.signalStrategy_ = |
365 remoting.SignalStrategy.create(this.onSignalingState_.bind(this)); | 347 remoting.SignalStrategy.create(this.onSignalingState_.bind(this)); |
366 | 348 |
367 remoting.identity.callWithToken(connectSignalingWithToken, this.onError_); | 349 remoting.identity.callWithToken(connectSignalingWithToken, this.app_.onError); |
368 }; | 350 }; |
369 | 351 |
370 /** | 352 /** |
371 * @private | 353 * @private |
372 * @param {remoting.SignalStrategy.State} state | 354 * @param {remoting.SignalStrategy.State} state |
373 */ | 355 */ |
374 remoting.SessionConnectorImpl.prototype.onSignalingState_ = function(state) { | 356 remoting.SessionConnectorImpl.prototype.onSignalingState_ = function(state) { |
375 switch (state) { | 357 switch (state) { |
376 case remoting.SignalStrategy.State.CONNECTED: | 358 case remoting.SignalStrategy.State.CONNECTED: |
377 // Proceed only if the connection hasn't been canceled. | 359 // Proceed only if the connection hasn't been canceled. |
378 if (this.hostJid_) { | 360 if (this.hostJid_) { |
379 this.createSession_(); | 361 this.createSession_(); |
380 } | 362 } |
381 break; | 363 break; |
382 | 364 |
383 case remoting.SignalStrategy.State.FAILED: | 365 case remoting.SignalStrategy.State.FAILED: |
384 this.onError_(this.signalStrategy_.getError()); | 366 this.app_.onError(this.signalStrategy_.getError()); |
385 break; | 367 break; |
386 } | 368 } |
387 }; | 369 }; |
388 | 370 |
389 /** | 371 /** |
390 * Continue an IT2Me connection once an access token has been obtained. | 372 * Continue an IT2Me connection once an access token has been obtained. |
391 * | 373 * |
392 * @param {string} token An OAuth2 access token. | 374 * @param {string} token An OAuth2 access token. |
393 * @return {void} Nothing. | 375 * @return {void} Nothing. |
394 * @private | 376 * @private |
(...skipping 24 matching lines...) Expand all Loading... |
419 if (host && host.data && host.data.jabberId && host.data.publicKey) { | 401 if (host && host.data && host.data.jabberId && host.data.publicKey) { |
420 this.hostJid_ = host.data.jabberId; | 402 this.hostJid_ = host.data.jabberId; |
421 this.hostPublicKey_ = host.data.publicKey; | 403 this.hostPublicKey_ = host.data.publicKey; |
422 this.hostDisplayName_ = this.hostJid_.split('/')[0]; | 404 this.hostDisplayName_ = this.hostJid_.split('/')[0]; |
423 this.connectSignaling_(); | 405 this.connectSignaling_(); |
424 return; | 406 return; |
425 } else { | 407 } else { |
426 console.error('Invalid "support-hosts" response from server.'); | 408 console.error('Invalid "support-hosts" response from server.'); |
427 } | 409 } |
428 } else { | 410 } else { |
429 this.onError_(this.translateSupportHostsError_(xhr.status)); | 411 this.app_.onError(this.translateSupportHostsError_(xhr.status)); |
430 } | 412 } |
431 }; | 413 }; |
432 | 414 |
433 /** | 415 /** |
434 * Creates ClientSession object. | 416 * Creates ClientSession object. |
435 */ | 417 */ |
436 remoting.SessionConnectorImpl.prototype.createSession_ = function() { | 418 remoting.SessionConnectorImpl.prototype.createSession_ = function() { |
437 // In some circumstances, the WCS <iframe> can get reloaded, which results | 419 // In some circumstances, the WCS <iframe> can get reloaded, which results |
438 // in a new clientJid and a new callback. In this case, remove the old | 420 // in a new clientJid and a new callback. In this case, remove the old |
439 // client plugin before instantiating a new one. | 421 // client plugin before instantiating a new one. |
440 if (this.clientSession_) { | 422 if (this.clientSession_) { |
441 this.clientSession_.removePlugin(); | 423 this.clientSession_.removePlugin(); |
442 this.clientSession_ = null; | 424 this.clientSession_ = null; |
443 } | 425 } |
444 | 426 |
445 var authenticationMethods = | 427 var authenticationMethods = |
446 'third_party,spake2_pair,spake2_hmac,spake2_plain'; | 428 'third_party,spake2_pair,spake2_hmac,spake2_plain'; |
447 this.clientSession_ = new remoting.ClientSession( | 429 this.clientSession_ = new remoting.ClientSession( |
448 this.signalStrategy_, this.clientContainer_, this.hostDisplayName_, | 430 this.signalStrategy_, this.clientContainer_, this.hostDisplayName_, |
449 this.passPhrase_, this.fetchPin_, this.fetchThirdPartyToken_, | 431 this.passPhrase_, this.fetchPin_, this.fetchThirdPartyToken_, |
450 authenticationMethods, this.hostId_, this.hostJid_, this.hostPublicKey_, | 432 authenticationMethods, this.hostId_, this.hostJid_, this.hostPublicKey_, |
451 this.connectionMode_, this.clientPairingId_, this.clientPairedSecret_); | 433 this.connectionMode_, this.clientPairingId_, this.clientPairedSecret_); |
452 this.clientSession_.logHostOfflineErrors(!this.refreshHostJidIfOffline_); | 434 this.clientSession_.logHostOfflineErrors(!this.refreshHostJidIfOffline_); |
453 this.clientSession_.addEventListener( | 435 this.clientSession_.addEventListener( |
454 remoting.ClientSession.Events.stateChanged, | 436 remoting.ClientSession.Events.stateChanged, |
455 this.bound_.onStateChange); | 437 this.bound_.onStateChange); |
456 this.clientSession_.createPluginAndConnect(this.onExtensionMessage_); | 438 this.clientSession_.createPluginAndConnect(this.app_.onExtensionMessage); |
457 }; | 439 }; |
458 | 440 |
459 /** | 441 /** |
460 * Handle a change in the state of the client session prior to successful | 442 * Handle a change in the state of the client session prior to successful |
461 * connection (after connection, this class no longer handles state change | 443 * connection (after connection, this class no longer handles state change |
462 * events). Errors that occur while connecting either trigger a reconnect | 444 * events). Errors that occur while connecting either trigger a reconnect |
463 * or notify the onError handler. | 445 * or notify the onError handler. |
464 * | 446 * |
465 * @param {remoting.ClientSession.StateEvent} event | 447 * @param {remoting.ClientSession.StateEvent} event |
466 * @return {void} Nothing. | 448 * @return {void} Nothing. |
467 * @private | 449 * @private |
468 */ | 450 */ |
469 remoting.SessionConnectorImpl.prototype.onStateChange_ = function(event) { | 451 remoting.SessionConnectorImpl.prototype.onStateChange_ = function(event) { |
470 switch (event.current) { | 452 switch (event.current) { |
471 case remoting.ClientSession.State.CONNECTED: | 453 case remoting.ClientSession.State.CONNECTED: |
472 // When the connection succeeds, deregister for state-change callbacks | 454 // When the connection succeeds, deregister for state-change callbacks |
473 // and pass the session to the onConnected callback. It is expected that | 455 // and pass the session to the onConnected callback. It is expected that |
474 // it will register a new state-change callback to handle disconnect | 456 // it will register a new state-change callback to handle disconnect |
475 // or error conditions. | 457 // or error conditions. |
476 this.clientSession_.removeEventListener( | 458 this.clientSession_.removeEventListener( |
477 remoting.ClientSession.Events.stateChanged, | 459 remoting.ClientSession.Events.stateChanged, |
478 this.bound_.onStateChange); | 460 this.bound_.onStateChange); |
479 | 461 |
480 base.dispose(this.reconnector_); | 462 base.dispose(this.reconnector_); |
481 if (this.connectionMode_ != remoting.ClientSession.Mode.IT2ME) { | 463 if (this.connectionMode_ != remoting.ClientSession.Mode.IT2ME) { |
482 this.reconnector_ = | 464 this.reconnector_ = |
483 new remoting.SmartReconnector(this, this.clientSession_); | 465 new remoting.SmartReconnector(this, this.clientSession_); |
484 } | 466 } |
485 this.onConnected_(this.clientSession_); | 467 this.app_.onConnected(this.clientSession_); |
486 break; | 468 break; |
487 | 469 |
488 case remoting.ClientSession.State.CREATED: | 470 case remoting.ClientSession.State.CREATED: |
489 console.log('Created plugin'); | 471 console.log('Created plugin'); |
490 break; | 472 break; |
491 | 473 |
492 case remoting.ClientSession.State.CONNECTING: | 474 case remoting.ClientSession.State.CONNECTING: |
493 console.log('Connecting as ' + remoting.identity.getCachedEmail()); | 475 console.log('Connecting as ' + remoting.identity.getCachedEmail()); |
494 break; | 476 break; |
495 | 477 |
496 case remoting.ClientSession.State.INITIALIZING: | 478 case remoting.ClientSession.State.INITIALIZING: |
497 console.log('Initializing connection'); | 479 console.log('Initializing connection'); |
498 break; | 480 break; |
499 | 481 |
500 case remoting.ClientSession.State.CLOSED: | 482 case remoting.ClientSession.State.CLOSED: |
501 // This class deregisters for state-change callbacks when the CONNECTED | 483 // This class deregisters for state-change callbacks when the CONNECTED |
502 // state is reached, so it only sees the CLOSED state in exceptional | 484 // state is reached, so it only sees the CLOSED state in exceptional |
503 // circumstances. For example, a CONNECTING -> CLOSED transition happens | 485 // circumstances. For example, a CONNECTING -> CLOSED transition happens |
504 // if the host closes the connection without an error message instead of | 486 // if the host closes the connection without an error message instead of |
505 // accepting it. Since there's no way of knowing exactly what went wrong, | 487 // accepting it. Since there's no way of knowing exactly what went wrong, |
506 // we rely on server-side logs in this case and report a generic error | 488 // we rely on server-side logs in this case and report a generic error |
507 // message. | 489 // message. |
508 this.onError_(remoting.Error.UNEXPECTED); | 490 this.app_.onError(remoting.Error.UNEXPECTED); |
509 break; | 491 break; |
510 | 492 |
511 case remoting.ClientSession.State.FAILED: | 493 case remoting.ClientSession.State.FAILED: |
512 var error = this.clientSession_.getError(); | 494 var error = this.clientSession_.getError(); |
513 console.error('Client plugin reported connection failed: ' + error); | 495 console.error('Client plugin reported connection failed: ' + error); |
514 if (error == null) { | 496 if (error == null) { |
515 error = remoting.Error.UNEXPECTED; | 497 error = remoting.Error.UNEXPECTED; |
516 } | 498 } |
517 if (error == remoting.Error.HOST_IS_OFFLINE && | 499 if (error == remoting.Error.HOST_IS_OFFLINE && |
518 this.refreshHostJidIfOffline_) { | 500 this.refreshHostJidIfOffline_) { |
519 // The plugin will be re-created when the host finished refreshing | 501 // The plugin will be re-created when the host finished refreshing |
520 remoting.hostList.refresh(this.onHostListRefresh_.bind(this)); | 502 remoting.hostList.refresh(this.onHostListRefresh_.bind(this)); |
521 } else { | 503 } else { |
522 this.onError_(error); | 504 this.app_.onError(error); |
523 } | 505 } |
524 break; | 506 break; |
525 | 507 |
526 default: | 508 default: |
527 console.error('Unexpected client plugin state: ' + event.current); | 509 console.error('Unexpected client plugin state: ' + event.current); |
528 // This should only happen if the web-app and client plugin get out of | 510 // This should only happen if the web-app and client plugin get out of |
529 // sync, and even then the version check should ensure compatibility. | 511 // sync, and even then the version check should ensure compatibility. |
530 this.onError_(remoting.Error.MISSING_PLUGIN); | 512 this.app_.onError(remoting.Error.MISSING_PLUGIN); |
531 } | 513 } |
532 }; | 514 }; |
533 | 515 |
534 /** | 516 /** |
535 * @param {boolean} success True if the host list was successfully refreshed; | 517 * @param {boolean} success True if the host list was successfully refreshed; |
536 * false if an error occurred. | 518 * false if an error occurred. |
537 * @private | 519 * @private |
538 */ | 520 */ |
539 remoting.SessionConnectorImpl.prototype.onHostListRefresh_ = function(success) { | 521 remoting.SessionConnectorImpl.prototype.onHostListRefresh_ = function(success) { |
540 if (success) { | 522 if (success) { |
541 var host = remoting.hostList.getHostForId(this.hostId_); | 523 var host = remoting.hostList.getHostForId(this.hostId_); |
542 if (host) { | 524 if (host) { |
543 this.connectMe2MeInternal_( | 525 this.connectMe2MeInternal_( |
544 host.hostId, host.jabberId, host.publicKey, host.hostName, | 526 host.hostId, host.jabberId, host.publicKey, host.hostName, |
545 this.fetchPin_, this.fetchThirdPartyToken_, | 527 this.fetchPin_, this.fetchThirdPartyToken_, |
546 this.clientPairingId_, this.clientPairedSecret_, false); | 528 this.clientPairingId_, this.clientPairedSecret_, false); |
547 return; | 529 return; |
548 } | 530 } |
549 } | 531 } |
550 this.onError_(remoting.Error.HOST_IS_OFFLINE); | 532 this.app_.onError(remoting.Error.HOST_IS_OFFLINE); |
551 }; | 533 }; |
552 | 534 |
553 /** | 535 /** |
554 * @param {number} error An HTTP error code returned by the support-hosts | 536 * @param {number} error An HTTP error code returned by the support-hosts |
555 * endpoint. | 537 * endpoint. |
556 * @return {remoting.Error} The equivalent remoting.Error code. | 538 * @return {remoting.Error} The equivalent remoting.Error code. |
557 * @private | 539 * @private |
558 */ | 540 */ |
559 remoting.SessionConnectorImpl.prototype.translateSupportHostsError_ = | 541 remoting.SessionConnectorImpl.prototype.translateSupportHostsError_ = |
560 function(error) { | 542 function(error) { |
(...skipping 22 matching lines...) Expand all Loading... |
583 | 565 |
584 /** | 566 /** |
585 * @constructor | 567 * @constructor |
586 * @implements {remoting.SessionConnectorFactory} | 568 * @implements {remoting.SessionConnectorFactory} |
587 */ | 569 */ |
588 remoting.DefaultSessionConnectorFactory = function() { | 570 remoting.DefaultSessionConnectorFactory = function() { |
589 }; | 571 }; |
590 | 572 |
591 /** | 573 /** |
592 * @param {HTMLElement} clientContainer Container element for the client view. | 574 * @param {HTMLElement} clientContainer Container element for the client view. |
593 * @param {function(remoting.ClientSession):void} onConnected Callback on | 575 * @param {remoting.Application} app The main application. |
594 * success. | |
595 * @param {function(remoting.Error):void} onError Callback on error. | |
596 * @param {function(string, string):boolean} onExtensionMessage The handler for | |
597 * protocol extension messages. Returns true if a message is recognized; | |
598 * false otherwise. | |
599 */ | 576 */ |
600 remoting.DefaultSessionConnectorFactory.prototype.createConnector = | 577 remoting.DefaultSessionConnectorFactory.prototype.createConnector = |
601 function(clientContainer, onConnected, onError, onExtensionMessage) { | 578 function(clientContainer, app) { |
602 return new remoting.SessionConnectorImpl( | 579 return new remoting.SessionConnectorImpl(clientContainer, app); |
603 clientContainer, onConnected, onError, onExtensionMessage); | |
604 }; | 580 }; |
OLD | NEW |