OLD | NEW |
1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. | 1 // Copyright (c) 2012 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 * Class handling creation and teardown of a remoting client session. | 7 * Class handling creation and teardown of a remoting client session. |
8 * | 8 * |
9 * The ClientSession class controls lifetime of the client plugin | 9 * The ClientSession class controls lifetime of the client plugin |
10 * object and provides the plugin with the functionality it needs to | 10 * object and provides the plugin with the functionality it needs to |
(...skipping 14 matching lines...) Expand all Loading... |
25 /** | 25 /** |
26 * Interval that determines how often the web-app should send a new access token | 26 * Interval that determines how often the web-app should send a new access token |
27 * to the host. | 27 * to the host. |
28 * | 28 * |
29 * @const | 29 * @const |
30 * @type {number} | 30 * @type {number} |
31 */ | 31 */ |
32 remoting.ACCESS_TOKEN_RESEND_INTERVAL_MS = 15 * 60 * 1000; | 32 remoting.ACCESS_TOKEN_RESEND_INTERVAL_MS = 15 * 60 * 1000; |
33 | 33 |
34 /** | 34 /** |
| 35 * @param {remoting.ClientPlugin} plugin |
35 * @param {remoting.Host} host The host to connect to. | 36 * @param {remoting.Host} host The host to connect to. |
36 * @param {remoting.SignalStrategy} signalStrategy Signal strategy. | 37 * @param {remoting.SignalStrategy} signalStrategy Signal strategy. |
37 * @param {remoting.CredentialsProvider} credentialsProvider | |
38 * The credentialsProvider to authenticate the client with the host. | |
39 * @param {HTMLElement} container Container element for the client view. | |
40 * @param {remoting.DesktopConnectedView.Mode} mode The mode of this connection. | 38 * @param {remoting.DesktopConnectedView.Mode} mode The mode of this connection. |
41 * @param {string} defaultRemapKeys The default set of remap keys, to use | |
42 * when the client doesn't define any. | |
43 * @constructor | 39 * @constructor |
44 * @extends {base.EventSourceImpl} | 40 * @extends {base.EventSourceImpl} |
45 * @implements {base.Disposable} | 41 * @implements {base.Disposable} |
46 */ | 42 */ |
47 remoting.ClientSession = function(host, signalStrategy, credentialsProvider, | 43 remoting.ClientSession = function(plugin, host, signalStrategy, mode) { |
48 container, mode, defaultRemapKeys) { | |
49 /** @private */ | 44 /** @private */ |
50 this.state_ = remoting.ClientSession.State.CREATED; | 45 this.state_ = remoting.ClientSession.State.CREATED; |
51 | 46 |
52 /** @private */ | 47 /** @private */ |
53 this.error_ = remoting.Error.NONE; | 48 this.error_ = remoting.Error.NONE; |
54 | 49 |
55 /** @private */ | 50 /** @private */ |
56 this.host_ = host; | 51 this.host_ = host; |
57 | 52 |
58 /** @private */ | 53 /** @private */ |
59 this.credentialsProvider_ = credentialsProvider; | 54 this.sessionId_ = ''; |
60 | 55 |
61 /** @private */ | 56 /** @private */ |
62 this.uiHandler_ = new remoting.DesktopConnectedView( | |
63 this, container, this.host_, mode, defaultRemapKeys, | |
64 this.onPluginInitialized_.bind(this)); | |
65 remoting.desktopConnectedView = this.uiHandler_; | |
66 | |
67 /** @private */ | |
68 this.sessionId_ = ''; | |
69 /** @private {remoting.ClientPlugin} */ | |
70 this.plugin_ = null; | |
71 /** @private */ | |
72 this.hasReceivedFrame_ = false; | 57 this.hasReceivedFrame_ = false; |
73 this.logToServer = new remoting.LogToServer(signalStrategy, mode); | 58 this.logToServer = new remoting.LogToServer(signalStrategy, mode); |
74 | 59 |
75 /** @private */ | 60 /** @private */ |
76 this.signalStrategy_ = signalStrategy; | 61 this.signalStrategy_ = signalStrategy; |
77 base.debug.assert(this.signalStrategy_.getState() == | 62 base.debug.assert(this.signalStrategy_.getState() == |
78 remoting.SignalStrategy.State.CONNECTED); | 63 remoting.SignalStrategy.State.CONNECTED); |
79 this.signalStrategy_.setIncomingStanzaCallback( | 64 this.signalStrategy_.setIncomingStanzaCallback( |
80 this.onIncomingMessage_.bind(this)); | 65 this.onIncomingMessage_.bind(this)); |
81 remoting.formatIq.setJids(this.signalStrategy_.getJid(), host.jabberId); | 66 remoting.formatIq.setJids(this.signalStrategy_.getJid(), host.jabberId); |
82 | 67 |
83 /** | 68 /** |
84 * Allow host-offline error reporting to be suppressed in situations where it | 69 * Allow host-offline error reporting to be suppressed in situations where it |
85 * would not be useful, for example, when using a cached host JID. | 70 * would not be useful, for example, when using a cached host JID. |
86 * | 71 * |
87 * @type {boolean} @private | 72 * @type {boolean} @private |
88 */ | 73 */ |
89 this.logHostOfflineErrors_ = true; | 74 this.logHostOfflineErrors_ = true; |
90 | 75 |
91 /** @private {remoting.GnubbyAuthHandler} */ | 76 /** @private {remoting.GnubbyAuthHandler} */ |
92 this.gnubbyAuthHandler_ = null; | 77 this.gnubbyAuthHandler_ = null; |
93 | 78 |
94 /** @private {remoting.CastExtensionHandler} */ | 79 /** @private {remoting.CastExtensionHandler} */ |
95 this.castExtensionHandler_ = null; | 80 this.castExtensionHandler_ = null; |
96 | 81 |
| 82 /** @private {remoting.ClientPlugin} */ |
| 83 this.plugin_ = plugin; |
| 84 plugin.setOnOutgoingIqHandler(this.sendIq_.bind(this)); |
| 85 plugin.setOnDebugMessageHandler(this.onDebugMessage_.bind(this)); |
| 86 plugin.setConnectionStatusUpdateHandler( |
| 87 this.onConnectionStatusUpdate_.bind(this)); |
| 88 plugin.setRouteChangedHandler(this.onRouteChanged_.bind(this)); |
| 89 plugin.setConnectionReadyHandler(this.onConnectionReady_.bind(this)); |
| 90 plugin.setCapabilitiesHandler(this.onSetCapabilities_.bind(this)); |
| 91 plugin.setGnubbyAuthHandler( |
| 92 this.processGnubbyAuthMessage_.bind(this)); |
| 93 plugin.setCastExtensionHandler( |
| 94 this.processCastExtensionMessage_.bind(this)); |
| 95 |
97 this.defineEvents(Object.keys(remoting.ClientSession.Events)); | 96 this.defineEvents(Object.keys(remoting.ClientSession.Events)); |
98 }; | 97 }; |
99 | 98 |
100 base.extend(remoting.ClientSession, base.EventSourceImpl); | 99 base.extend(remoting.ClientSession, base.EventSourceImpl); |
101 | 100 |
102 /** @enum {string} */ | 101 /** @enum {string} */ |
103 remoting.ClientSession.Events = { | 102 remoting.ClientSession.Events = { |
104 stateChanged: 'stateChanged', | 103 stateChanged: 'stateChanged', |
105 videoChannelStateChanged: 'videoChannelStateChanged', | 104 videoChannelStateChanged: 'videoChannelStateChanged', |
106 }; | 105 }; |
(...skipping 135 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
242 * the client and host. | 241 * the client and host. |
243 */ | 242 */ |
244 remoting.ClientSession.prototype.hasCapability = function(capability) { | 243 remoting.ClientSession.prototype.hasCapability = function(capability) { |
245 if (this.capabilities_ == null) | 244 if (this.capabilities_ == null) |
246 return false; | 245 return false; |
247 | 246 |
248 return this.capabilities_.indexOf(capability) > -1; | 247 return this.capabilities_.indexOf(capability) > -1; |
249 }; | 248 }; |
250 | 249 |
251 /** | 250 /** |
252 * Adds <embed> element to the UI container and readies the session object. | 251 * @return {void} Nothing. |
253 * | |
254 * @param {function(string, string):boolean} onExtensionMessage The handler for | |
255 * protocol extension messages. Returns true if a message is recognized; | |
256 * false otherwise. | |
257 * @param {Array<string>} requiredCapabilities A list of capabilities | |
258 * required by this application. | |
259 */ | 252 */ |
260 remoting.ClientSession.prototype.createPluginAndConnect = | 253 remoting.ClientSession.prototype.removePlugin = function() { |
261 function(onExtensionMessage, requiredCapabilities) { | 254 this.plugin_ = null; |
262 this.uiHandler_.createPluginAndConnect(onExtensionMessage, | |
263 requiredCapabilities); | |
264 }; | 255 }; |
265 | 256 |
266 /** | 257 /** |
267 * @param {remoting.Error} error | |
268 * @param {remoting.ClientPlugin} plugin | |
269 */ | |
270 remoting.ClientSession.prototype.onPluginInitialized_ = function( | |
271 error, plugin) { | |
272 if (error != remoting.Error.NONE) { | |
273 this.resetWithError_(error); | |
274 } | |
275 | |
276 this.plugin_ = plugin; | |
277 plugin.setOnOutgoingIqHandler(this.sendIq_.bind(this)); | |
278 plugin.setOnDebugMessageHandler(this.onDebugMessage_.bind(this)); | |
279 | |
280 plugin.setConnectionStatusUpdateHandler( | |
281 this.onConnectionStatusUpdate_.bind(this)); | |
282 plugin.setRouteChangedHandler(this.onRouteChanged_.bind(this)); | |
283 plugin.setConnectionReadyHandler(this.onConnectionReady_.bind(this)); | |
284 plugin.setCapabilitiesHandler(this.onSetCapabilities_.bind(this)); | |
285 plugin.setGnubbyAuthHandler( | |
286 this.processGnubbyAuthMessage_.bind(this)); | |
287 plugin.setCastExtensionHandler( | |
288 this.processCastExtensionMessage_.bind(this)); | |
289 | |
290 this.plugin_.connect( | |
291 this.host_, this.signalStrategy_.getJid(), this.credentialsProvider_); | |
292 }; | |
293 | |
294 /** | |
295 * @param {remoting.Error} error | |
296 */ | |
297 remoting.ClientSession.prototype.resetWithError_ = function(error) { | |
298 this.signalStrategy_.setIncomingStanzaCallback(null); | |
299 this.removePlugin(); | |
300 this.error_ = error; | |
301 this.setState_(remoting.ClientSession.State.FAILED); | |
302 }; | |
303 | |
304 /** | |
305 * Deletes the <embed> element from the container, without sending a | |
306 * session_terminate request. This is to be called when the session was | |
307 * disconnected by the Host. | |
308 * | |
309 * @return {void} Nothing. | |
310 */ | |
311 remoting.ClientSession.prototype.removePlugin = function() { | |
312 this.uiHandler_.removePlugin(); | |
313 this.plugin_ = null; | |
314 remoting.desktopConnectedView = null; | |
315 }; | |
316 | |
317 /** | |
318 * Disconnect the current session with a particular |error|. The session will | 258 * Disconnect the current session with a particular |error|. The session will |
319 * raise a |stateChanged| event in response to it. The caller should then call | 259 * raise a |stateChanged| event in response to it. The caller should then call |
320 * dispose() to remove and destroy the <embed> element. | 260 * dispose() to remove and destroy the <embed> element. |
321 * | 261 * |
322 * @param {remoting.Error} error The reason for the disconnection. Use | 262 * @param {remoting.Error} error The reason for the disconnection. Use |
323 * remoting.Error.NONE if there is no error. | 263 * remoting.Error.NONE if there is no error. |
324 * @return {void} Nothing. | 264 * @return {void} Nothing. |
325 */ | 265 */ |
326 remoting.ClientSession.prototype.disconnect = function(error) { | 266 remoting.ClientSession.prototype.disconnect = function(error) { |
327 var state = (error == remoting.Error.NONE) ? | 267 var state = (error == remoting.Error.NONE) ? |
(...skipping 114 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
442 * Callback that the plugin invokes to indicate that the connection | 382 * Callback that the plugin invokes to indicate that the connection |
443 * status has changed. | 383 * status has changed. |
444 * | 384 * |
445 * @param {number} status The plugin's status. | 385 * @param {number} status The plugin's status. |
446 * @param {number} error The plugin's error state, if any. | 386 * @param {number} error The plugin's error state, if any. |
447 * @private | 387 * @private |
448 */ | 388 */ |
449 remoting.ClientSession.prototype.onConnectionStatusUpdate_ = | 389 remoting.ClientSession.prototype.onConnectionStatusUpdate_ = |
450 function(status, error) { | 390 function(status, error) { |
451 if (status == remoting.ClientSession.State.CONNECTED) { | 391 if (status == remoting.ClientSession.State.CONNECTED) { |
452 this.uiHandler_.updateClientSessionUi_(this); | 392 remoting.desktopConnectedView.updateClientSessionUi_(this); |
453 | 393 |
454 } else if (status == remoting.ClientSession.State.FAILED) { | 394 } else if (status == remoting.ClientSession.State.FAILED) { |
455 switch (error) { | 395 switch (error) { |
456 case remoting.ClientSession.ConnectionError.HOST_IS_OFFLINE: | 396 case remoting.ClientSession.ConnectionError.HOST_IS_OFFLINE: |
457 this.error_ = remoting.Error.HOST_IS_OFFLINE; | 397 this.error_ = remoting.Error.HOST_IS_OFFLINE; |
458 break; | 398 break; |
459 case remoting.ClientSession.ConnectionError.SESSION_REJECTED: | 399 case remoting.ClientSession.ConnectionError.SESSION_REJECTED: |
460 this.error_ = remoting.Error.INVALID_ACCESS_CODE; | 400 this.error_ = remoting.Error.INVALID_ACCESS_CODE; |
461 break; | 401 break; |
462 case remoting.ClientSession.ConnectionError.INCOMPATIBLE_PROTOCOL: | 402 case remoting.ClientSession.ConnectionError.INCOMPATIBLE_PROTOCOL: |
(...skipping 38 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
501 // TODO(jamiewalch): Currently, the logic for determining whether or not the | 441 // TODO(jamiewalch): Currently, the logic for determining whether or not the |
502 // connection is available is based solely on whether or not any video frames | 442 // connection is available is based solely on whether or not any video frames |
503 // have been received recently. which leads to poor UX on slow connections. | 443 // have been received recently. which leads to poor UX on slow connections. |
504 // Re-enable this once crbug.com/435315 has been fixed. | 444 // Re-enable this once crbug.com/435315 has been fixed. |
505 var ignoreVideoChannelState = true; | 445 var ignoreVideoChannelState = true; |
506 if (ignoreVideoChannelState) { | 446 if (ignoreVideoChannelState) { |
507 console.log('Video channel ' + (ready ? '' : 'not ') + 'ready.'); | 447 console.log('Video channel ' + (ready ? '' : 'not ') + 'ready.'); |
508 return; | 448 return; |
509 } | 449 } |
510 | 450 |
511 this.uiHandler_.onConnectionReady(ready); | 451 remoting.desktopConnectedView.onConnectionReady(ready); |
512 | 452 |
513 this.raiseEvent(remoting.ClientSession.Events.videoChannelStateChanged, | 453 this.raiseEvent(remoting.ClientSession.Events.videoChannelStateChanged, |
514 ready); | 454 ready); |
515 }; | 455 }; |
516 | 456 |
517 /** | 457 /** |
518 * Called when the client-host capabilities negotiation is complete. | 458 * Called when the client-host capabilities negotiation is complete. |
519 * TODO(kelvinp): Move this function out of ClientSession. | 459 * TODO(kelvinp): Move this function out of ClientSession. |
520 * | 460 * |
521 * @param {!Array<string>} capabilities The set of capabilities negotiated | 461 * @param {!Array<string>} capabilities The set of capabilities negotiated |
522 * between the client and host. | 462 * between the client and host. |
523 * @return {void} Nothing. | 463 * @return {void} Nothing. |
524 * @private | 464 * @private |
525 */ | 465 */ |
526 remoting.ClientSession.prototype.onSetCapabilities_ = function(capabilities) { | 466 remoting.ClientSession.prototype.onSetCapabilities_ = function(capabilities) { |
527 if (this.capabilities_ != null) { | 467 if (this.capabilities_ != null) { |
528 console.error('onSetCapabilities_() is called more than once'); | 468 console.error('onSetCapabilities_() is called more than once'); |
529 return; | 469 return; |
530 } | 470 } |
531 | 471 |
532 this.capabilities_ = capabilities; | 472 this.capabilities_ = capabilities; |
533 if (this.hasCapability(remoting.ClientSession.Capability.GOOGLE_DRIVE)) { | 473 if (this.hasCapability(remoting.ClientSession.Capability.GOOGLE_DRIVE)) { |
534 this.sendGoogleDriveAccessToken_(); | 474 this.sendGoogleDriveAccessToken_(); |
535 } | 475 } |
536 if (this.hasCapability( | 476 if (this.hasCapability( |
537 remoting.ClientSession.Capability.VIDEO_RECORDER)) { | 477 remoting.ClientSession.Capability.VIDEO_RECORDER)) { |
538 this.uiHandler_.initVideoFrameRecorder(); | 478 remoting.desktopConnectedView.initVideoFrameRecorder(); |
539 } | 479 } |
540 }; | 480 }; |
541 | 481 |
542 /** | 482 /** |
543 * @param {remoting.ClientSession.State} newState The new state for the session. | 483 * @param {remoting.ClientSession.State} newState The new state for the session. |
544 * @return {void} Nothing. | 484 * @return {void} Nothing. |
545 * @private | 485 * @private |
546 */ | 486 */ |
547 remoting.ClientSession.prototype.setState_ = function(newState) { | 487 remoting.ClientSession.prototype.setState_ = function(newState) { |
548 var oldState = this.state_; | 488 var oldState = this.state_; |
(...skipping 118 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
667 console.error('Received unexpected gnubby message'); | 607 console.error('Received unexpected gnubby message'); |
668 } | 608 } |
669 }; | 609 }; |
670 | 610 |
671 /** | 611 /** |
672 * Create a gnubby auth handler and inform the host that gnubby auth is | 612 * Create a gnubby auth handler and inform the host that gnubby auth is |
673 * supported. | 613 * supported. |
674 * @private | 614 * @private |
675 */ | 615 */ |
676 remoting.ClientSession.prototype.createGnubbyAuthHandler_ = function() { | 616 remoting.ClientSession.prototype.createGnubbyAuthHandler_ = function() { |
677 if (this.uiHandler_.getMode() == remoting.DesktopConnectedView.Mode.ME2ME) { | 617 if (remoting.desktopConnectedView.getMode() == |
| 618 remoting.DesktopConnectedView.Mode.ME2ME) { |
678 this.gnubbyAuthHandler_ = new remoting.GnubbyAuthHandler(this); | 619 this.gnubbyAuthHandler_ = new remoting.GnubbyAuthHandler(this); |
679 // TODO(psj): Move to more generic capabilities mechanism. | 620 // TODO(psj): Move to more generic capabilities mechanism. |
680 this.sendGnubbyAuthMessage({'type': 'control', 'option': 'auth-v1'}); | 621 this.sendGnubbyAuthMessage({'type': 'control', 'option': 'auth-v1'}); |
681 } | 622 } |
682 }; | 623 }; |
683 | 624 |
684 /** | 625 /** |
685 * Timer callback to send the access token to the host. | 626 * Timer callback to send the access token to the host. |
686 * @private | 627 * @private |
687 */ | 628 */ |
(...skipping 46 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
734 } | 675 } |
735 }; | 676 }; |
736 | 677 |
737 /** | 678 /** |
738 * Create a CastExtensionHandler and inform the host that cast extension | 679 * Create a CastExtensionHandler and inform the host that cast extension |
739 * is supported. | 680 * is supported. |
740 * @private | 681 * @private |
741 */ | 682 */ |
742 remoting.ClientSession.prototype.createCastExtensionHandler_ = function() { | 683 remoting.ClientSession.prototype.createCastExtensionHandler_ = function() { |
743 if (remoting.app.hasCapability(remoting.ClientSession.Capability.CAST) && | 684 if (remoting.app.hasCapability(remoting.ClientSession.Capability.CAST) && |
744 this.uiHandler_.getMode() == remoting.DesktopConnectedView.Mode.ME2ME) { | 685 remoting.desktopConnectedView.getMode() == |
| 686 remoting.DesktopConnectedView.Mode.ME2ME) { |
745 this.castExtensionHandler_ = new remoting.CastExtensionHandler(this); | 687 this.castExtensionHandler_ = new remoting.CastExtensionHandler(this); |
746 } | 688 } |
747 }; | 689 }; |
748 | 690 |
749 /** | 691 /** |
750 * Handles protocol extension messages. | 692 * Handles protocol extension messages. |
751 * @param {string} type Type of extension message. | 693 * @param {string} type Type of extension message. |
752 * @param {Object} message The parsed extension message data. | 694 * @param {Object} message The parsed extension message data. |
753 * @return {boolean} True if the message was recognized, false otherwise. | 695 * @return {boolean} True if the message was recognized, false otherwise. |
754 */ | 696 */ |
755 remoting.ClientSession.prototype.handleExtensionMessage = | 697 remoting.ClientSession.prototype.handleExtensionMessage = |
756 function(type, message) { | 698 function(type, message) { |
757 if (this.uiHandler_.handleExtensionMessage(type, message)) { | 699 if (remoting.desktopConnectedView.handleExtensionMessage(type, message)) { |
758 return true; | 700 return true; |
759 } | 701 } |
760 return false; | 702 return false; |
761 }; | 703 }; |
762 | 704 |
763 /** | 705 /** |
764 * Enables or disables rendering of dirty regions for debugging. | 706 * Enables or disables rendering of dirty regions for debugging. |
765 * @param {boolean} enable True to enable rendering. | 707 * @param {boolean} enable True to enable rendering. |
766 */ | 708 */ |
767 remoting.ClientSession.prototype.enableDebugRegion = function(enable) { | 709 remoting.ClientSession.prototype.enableDebugRegion = function(enable) { |
768 if (enable) { | 710 if (enable) { |
769 this.plugin_.setDebugDirtyRegionHandler( | 711 this.plugin_.setDebugDirtyRegionHandler( |
770 this.uiHandler_.handleDebugRegion.bind(this.uiHandler_)); | 712 remoting.desktopConnectedView.handleDebugRegion.bind( |
| 713 remoting.desktopConnectedView)); |
771 } else { | 714 } else { |
772 this.plugin_.setDebugDirtyRegionHandler(null); | 715 this.plugin_.setDebugDirtyRegionHandler(null); |
773 } | 716 } |
774 } | 717 } |
OLD | NEW |