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 12 matching lines...) Expand all Loading... | |
23 var remoting = remoting || {}; | 23 var remoting = remoting || {}; |
24 | 24 |
25 /** | 25 /** |
26 * True if Cast capability is supported. | 26 * True if Cast capability is supported. |
27 * | 27 * |
28 * @type {boolean} | 28 * @type {boolean} |
29 */ | 29 */ |
30 remoting.enableCast = false; | 30 remoting.enableCast = false; |
31 | 31 |
32 /** | 32 /** |
33 * @param {remoting.SignalStrategy} signalStrategy Signal strategy. | |
33 * @param {HTMLElement} container Container element for the client view. | 34 * @param {HTMLElement} container Container element for the client view. |
34 * @param {string} hostDisplayName A human-readable name for the host. | 35 * @param {string} hostDisplayName A human-readable name for the host. |
35 * @param {string} accessCode The IT2Me access code. Blank for Me2Me. | 36 * @param {string} accessCode The IT2Me access code. Blank for Me2Me. |
36 * @param {function(boolean, function(string): void): void} fetchPin | 37 * @param {function(boolean, function(string): void): void} fetchPin |
37 * Called by Me2Me connections when a PIN needs to be obtained | 38 * Called by Me2Me connections when a PIN needs to be obtained |
38 * interactively. | 39 * interactively. |
39 * @param {function(string, string, string, | 40 * @param {function(string, string, string, |
40 * function(string, string): void): void} | 41 * function(string, string): void): void} |
41 * fetchThirdPartyToken Called by Me2Me connections when a third party | 42 * fetchThirdPartyToken Called by Me2Me connections when a third party |
42 * authentication token must be obtained. | 43 * authentication token must be obtained. |
43 * @param {string} authenticationMethods Comma-separated list of | 44 * @param {string} authenticationMethods Comma-separated list of |
44 * authentication methods the client should attempt to use. | 45 * authentication methods the client should attempt to use. |
45 * @param {string} hostId The host identifier for Me2Me, or empty for IT2Me. | 46 * @param {string} hostId The host identifier for Me2Me, or empty for IT2Me. |
46 * Mixed into authentication hashes for some authentication methods. | 47 * Mixed into authentication hashes for some authentication methods. |
47 * @param {string} hostJid The jid of the host to connect to. | 48 * @param {string} hostJid The jid of the host to connect to. |
48 * @param {string} hostPublicKey The base64 encoded version of the host's | 49 * @param {string} hostPublicKey The base64 encoded version of the host's |
49 * public key. | 50 * public key. |
50 * @param {remoting.ClientSession.Mode} mode The mode of this connection. | 51 * @param {remoting.ClientSession.Mode} mode The mode of this connection. |
51 * @param {string} clientPairingId For paired Me2Me connections, the | 52 * @param {string} clientPairingId For paired Me2Me connections, the |
52 * pairing id for this client, as issued by the host. | 53 * pairing id for this client, as issued by the host. |
53 * @param {string} clientPairedSecret For paired Me2Me connections, the | 54 * @param {string} clientPairedSecret For paired Me2Me connections, the |
54 * paired secret for this client, as issued by the host. | 55 * paired secret for this client, as issued by the host. |
55 * @constructor | 56 * @constructor |
56 * @extends {base.EventSource} | 57 * @extends {base.EventSource} |
57 */ | 58 */ |
58 remoting.ClientSession = function(container, hostDisplayName, accessCode, | 59 remoting.ClientSession = function(signalStrategy, container, hostDisplayName, |
59 fetchPin, fetchThirdPartyToken, | 60 accessCode, fetchPin, fetchThirdPartyToken, |
60 authenticationMethods, hostId, hostJid, | 61 authenticationMethods, hostId, hostJid, |
61 hostPublicKey, mode, clientPairingId, | 62 hostPublicKey, mode, clientPairingId, |
62 clientPairedSecret) { | 63 clientPairedSecret) { |
63 /** @private */ | 64 /** @private */ |
64 this.state_ = remoting.ClientSession.State.CREATED; | 65 this.state_ = remoting.ClientSession.State.CREATED; |
65 | 66 |
66 /** @private */ | 67 /** @private */ |
67 this.error_ = remoting.Error.NONE; | 68 this.error_ = remoting.Error.NONE; |
68 | 69 |
69 /** @type {HTMLElement} | 70 /** @type {HTMLElement} |
(...skipping 30 matching lines...) Expand all Loading... | |
100 /** @private */ | 101 /** @private */ |
101 this.shrinkToFit_ = true; | 102 this.shrinkToFit_ = true; |
102 /** @private */ | 103 /** @private */ |
103 this.resizeToClient_ = true; | 104 this.resizeToClient_ = true; |
104 /** @private */ | 105 /** @private */ |
105 this.remapKeys_ = ''; | 106 this.remapKeys_ = ''; |
106 /** @private */ | 107 /** @private */ |
107 this.hasReceivedFrame_ = false; | 108 this.hasReceivedFrame_ = false; |
108 this.logToServer = new remoting.LogToServer(); | 109 this.logToServer = new remoting.LogToServer(); |
109 | 110 |
111 /** @private */ | |
112 this.signalStrategy_ = signalStrategy; | |
113 base.debug.assert(this.signalStrategy_.getState() == | |
114 remoting.SignalStrategy.State.CONNECTED); | |
115 this.signalStrategy_.setIncomingStanzaCallback( | |
116 this.onIncomingMessage_.bind(this)); | |
117 remoting.formatIq.setJids(this.signalStrategy_.getJid(), hostJid); | |
118 | |
110 /** @type {number?} @private */ | 119 /** @type {number?} @private */ |
111 this.notifyClientResolutionTimer_ = null; | 120 this.notifyClientResolutionTimer_ = null; |
112 /** @type {number?} @private */ | 121 /** @type {number?} @private */ |
113 this.bumpScrollTimer_ = null; | 122 this.bumpScrollTimer_ = null; |
114 | 123 |
115 // Bump-scroll test variables. Override to use a fake value for the width | 124 // Bump-scroll test variables. Override to use a fake value for the width |
116 // and height of the client plugin so that bump-scrolling can be tested | 125 // and height of the client plugin so that bump-scrolling can be tested |
117 // without relying on the actual size of the host desktop. | 126 // without relying on the actual size of the host desktop. |
118 /** @type {number} @private */ | 127 /** @type {number} @private */ |
119 this.pluginWidthForBumpScrollTesting = 0; | 128 this.pluginWidthForBumpScrollTesting = 0; |
(...skipping 363 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
483 'focus', this.callPluginGotFocus_, false); | 492 'focus', this.callPluginGotFocus_, false); |
484 this.plugin_.element().addEventListener( | 493 this.plugin_.element().addEventListener( |
485 'blur', this.callPluginLostFocus_, false); | 494 'blur', this.callPluginLostFocus_, false); |
486 this.plugin_.element().focus(); | 495 this.plugin_.element().focus(); |
487 }; | 496 }; |
488 | 497 |
489 /** | 498 /** |
490 * @param {remoting.Error} error | 499 * @param {remoting.Error} error |
491 */ | 500 */ |
492 remoting.ClientSession.prototype.resetWithError_ = function(error) { | 501 remoting.ClientSession.prototype.resetWithError_ = function(error) { |
502 this.signalStrategy_.setIncomingStanzaCallback(null); | |
493 this.plugin_.cleanup(); | 503 this.plugin_.cleanup(); |
494 this.plugin_ = null; | 504 this.plugin_ = null; |
495 this.error_ = error; | 505 this.error_ = error; |
496 this.setState_(remoting.ClientSession.State.FAILED); | 506 this.setState_(remoting.ClientSession.State.FAILED); |
497 } | 507 } |
498 | 508 |
499 /** | 509 /** |
500 * @param {boolean} initialized | 510 * @param {boolean} initialized |
501 */ | 511 */ |
502 remoting.ClientSession.prototype.onPluginInitialized_ = function(initialized) { | 512 remoting.ClientSession.prototype.onPluginInitialized_ = function(initialized) { |
(...skipping 17 matching lines...) Expand all Loading... | |
520 } else if (this.mode_ != remoting.ClientSession.Mode.ME2ME) { | 530 } else if (this.mode_ != remoting.ClientSession.Mode.ME2ME) { |
521 var sendCadElement = document.getElementById('send-ctrl-alt-del'); | 531 var sendCadElement = document.getElementById('send-ctrl-alt-del'); |
522 sendCadElement.hidden = true; | 532 sendCadElement.hidden = true; |
523 } | 533 } |
524 | 534 |
525 // Apply customized key remappings if the plugin supports remapKeys. | 535 // Apply customized key remappings if the plugin supports remapKeys. |
526 if (this.plugin_.hasFeature(remoting.ClientPlugin.Feature.REMAP_KEY)) { | 536 if (this.plugin_.hasFeature(remoting.ClientPlugin.Feature.REMAP_KEY)) { |
527 this.applyRemapKeys_(true); | 537 this.applyRemapKeys_(true); |
528 } | 538 } |
529 | 539 |
530 | |
531 // Enable MediaSource-based rendering on Chrome 37 and above. | 540 // Enable MediaSource-based rendering on Chrome 37 and above. |
532 var chromeVersionMajor = | 541 var chromeVersionMajor = |
533 parseInt((remoting.getChromeVersion() || '0').split('.')[0], 10); | 542 parseInt((remoting.getChromeVersion() || '0').split('.')[0], 10); |
534 if (chromeVersionMajor >= 37 && | 543 if (chromeVersionMajor >= 37 && |
535 this.plugin_.hasFeature( | 544 this.plugin_.hasFeature( |
536 remoting.ClientPlugin.Feature.MEDIA_SOURCE_RENDERING)) { | 545 remoting.ClientPlugin.Feature.MEDIA_SOURCE_RENDERING)) { |
537 this.video_ = /** @type {HTMLMediaElement} */( | 546 this.video_ = /** @type {HTMLMediaElement} */( |
538 this.container_.querySelector('video')); | 547 this.container_.querySelector('video')); |
539 // Make sure that the <video> element is hidden until we get the first | 548 // Make sure that the <video> element is hidden until we get the first |
540 // frame. | 549 // frame. |
(...skipping 92 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
633 this.error_ = error; | 642 this.error_ = error; |
634 this.setState_(state); | 643 this.setState_(state); |
635 }; | 644 }; |
636 | 645 |
637 /** | 646 /** |
638 * Deletes the <embed> element from the container and disconnects. | 647 * Deletes the <embed> element from the container and disconnects. |
639 * | 648 * |
640 * @return {void} Nothing. | 649 * @return {void} Nothing. |
641 */ | 650 */ |
642 remoting.ClientSession.prototype.cleanup = function() { | 651 remoting.ClientSession.prototype.cleanup = function() { |
643 remoting.wcsSandbox.setOnIq(null); | |
644 this.sendIq_( | 652 this.sendIq_( |
645 '<cli:iq ' + | 653 '<cli:iq ' + |
646 'to="' + this.hostJid_ + '" ' + | 654 'to="' + this.hostJid_ + '" ' + |
647 'type="set" ' + | 655 'type="set" ' + |
648 'id="session-terminate" ' + | 656 'id="session-terminate" ' + |
649 'xmlns:cli="jabber:client">' + | 657 'xmlns:cli="jabber:client">' + |
650 '<jingle ' + | 658 '<jingle ' + |
651 'xmlns="urn:xmpp:jingle:1" ' + | 659 'xmlns="urn:xmpp:jingle:1" ' + |
652 'action="session-terminate" ' + | 660 'action="session-terminate" ' + |
653 'sid="' + this.sessionId_ + '">' + | 661 'sid="' + this.sessionId_ + '">' + |
(...skipping 172 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
826 }; | 834 }; |
827 | 835 |
828 /** | 836 /** |
829 * @return {boolean} Whether the client has received a video buffer. | 837 * @return {boolean} Whether the client has received a video buffer. |
830 */ | 838 */ |
831 remoting.ClientSession.prototype.hasReceivedFrame = function() { | 839 remoting.ClientSession.prototype.hasReceivedFrame = function() { |
832 return this.hasReceivedFrame_; | 840 return this.hasReceivedFrame_; |
833 }; | 841 }; |
834 | 842 |
835 /** | 843 /** |
836 * Sends an IQ stanza via the http xmpp proxy. | 844 * Sends a signaling message. |
837 * | 845 * |
838 * @private | 846 * @private |
839 * @param {string} msg XML string of IQ stanza to send to server. | 847 * @param {string} message XML string of IQ stanza to send to server. |
840 * @return {void} Nothing. | 848 * @return {void} Nothing. |
841 */ | 849 */ |
842 remoting.ClientSession.prototype.sendIq_ = function(msg) { | 850 remoting.ClientSession.prototype.sendIq_ = function(message) { |
843 // Extract the session id, so we can close the session later. | 851 // Extract the session id, so we can close the session later. |
844 var parser = new DOMParser(); | 852 var parser = new DOMParser(); |
845 var iqNode = parser.parseFromString(msg, 'text/xml').firstChild; | 853 var iqNode = parser.parseFromString(message, 'text/xml').firstChild; |
846 var jingleNode = iqNode.firstChild; | 854 var jingleNode = iqNode.firstChild; |
847 if (jingleNode) { | 855 if (jingleNode) { |
848 var action = jingleNode.getAttribute('action'); | 856 var action = jingleNode.getAttribute('action'); |
849 if (jingleNode.nodeName == 'jingle' && action == 'session-initiate') { | 857 if (jingleNode.nodeName == 'jingle' && action == 'session-initiate') { |
850 this.sessionId_ = jingleNode.getAttribute('sid'); | 858 this.sessionId_ = jingleNode.getAttribute('sid'); |
851 } | 859 } |
852 } | 860 } |
853 | 861 |
854 // HACK: Add 'x' prefix to the IDs of the outgoing messages to make sure that | 862 console.log(remoting.timestamp(), remoting.formatIq.prettifySendIq(message)); |
855 // stanza IDs used by host and client do not match. This is necessary to | 863 if (this.signalStrategy_.getState() != |
856 // workaround bug in the signaling endpoint used by chromoting. | 864 remoting.SignalStrategy.State.CONNECTED) { |
857 // TODO(sergeyu): Remove this hack once the server-side bug is fixed. | 865 console.log("Message above is dropped because signaling is not connected."); |
858 var type = iqNode.getAttribute('type'); | 866 return; |
859 if (type == 'set') { | |
860 var id = iqNode.getAttribute('id'); | |
861 iqNode.setAttribute('id', 'x' + id); | |
862 msg = (new XMLSerializer()).serializeToString(iqNode); | |
863 } | 867 } |
864 | 868 |
865 console.log(remoting.timestamp(), remoting.formatIq.prettifySendIq(msg)); | 869 this.signalStrategy_.sendMessage(message); |
870 }; | |
866 | 871 |
867 // Send the stanza. | 872 /** @param{Element} message */ |
kelvinp
2014/09/03 18:53:29
Nit space between @param and {Element}
Add private
Sergey Ulanov
2014/09/03 23:25:12
Done.
| |
868 remoting.wcsSandbox.sendIq(msg); | 873 remoting.ClientSession.prototype.onIncomingMessage_ = function(message) { |
869 }; | 874 if (!this.plugin_) { |
875 return; | |
876 } | |
877 var formatted = new XMLSerializer().serializeToString(message); | |
878 console.log(remoting.timestamp(), | |
879 remoting.formatIq.prettifyReceiveIq(formatted)); | |
880 this.plugin_.onIncomingIq(formatted); | |
881 } | |
870 | 882 |
871 remoting.ClientSession.prototype.initiateConnection_ = function() { | 883 remoting.ClientSession.prototype.initiateConnection_ = function() { |
872 /** @type {remoting.ClientSession} */ | 884 /** @type {remoting.ClientSession} */ |
873 var that = this; | 885 var that = this; |
874 | 886 |
875 remoting.wcsSandbox.connect(onWcsConnected, this.resetWithError_.bind(this)); | 887 /** @param {string} sharedSecret Shared secret. */ |
888 function onSharedSecretReceived(sharedSecret) { | |
889 that.plugin_.connect( | |
890 that.hostJid_, that.hostPublicKey_, that.signalStrategy_.getJid(), | |
891 sharedSecret, that.authenticationMethods_, that.hostId_, | |
892 that.clientPairingId_, that.clientPairedSecret_); | |
893 }; | |
876 | 894 |
877 /** @param {string} localJid Local JID. */ | 895 this.getSharedSecret_(onSharedSecretReceived); |
878 function onWcsConnected(localJid) { | |
879 that.connectPluginToWcs_(localJid); | |
880 that.getSharedSecret_(onSharedSecretReceived.bind(null, localJid)); | |
881 } | |
882 | |
883 /** @param {string} localJid Local JID. | |
884 * @param {string} sharedSecret Shared secret. */ | |
885 function onSharedSecretReceived(localJid, sharedSecret) { | |
886 that.plugin_.connect( | |
887 that.hostJid_, that.hostPublicKey_, localJid, sharedSecret, | |
888 that.authenticationMethods_, that.hostId_, that.clientPairingId_, | |
889 that.clientPairedSecret_); | |
890 }; | |
891 } | 896 } |
892 | 897 |
893 /** | 898 /** |
894 * Connects the plugin to WCS. | |
895 * | |
896 * @private | |
897 * @param {string} localJid Local JID. | |
898 * @return {void} Nothing. | |
899 */ | |
900 remoting.ClientSession.prototype.connectPluginToWcs_ = function(localJid) { | |
901 remoting.formatIq.setJids(localJid, this.hostJid_); | |
902 var forwardIq = this.plugin_.onIncomingIq.bind(this.plugin_); | |
903 /** @param {string} stanza The IQ stanza received. */ | |
904 var onIncomingIq = function(stanza) { | |
905 // HACK: Remove 'x' prefix added to the id in sendIq_(). | |
906 try { | |
907 var parser = new DOMParser(); | |
908 var iqNode = parser.parseFromString(stanza, 'text/xml').firstChild; | |
909 var type = iqNode.getAttribute('type'); | |
910 var id = iqNode.getAttribute('id'); | |
911 if (type != 'set' && id.charAt(0) == 'x') { | |
912 iqNode.setAttribute('id', id.substr(1)); | |
913 stanza = (new XMLSerializer()).serializeToString(iqNode); | |
914 } | |
915 } catch (err) { | |
916 // Pass message as is when it is malformed. | |
917 } | |
918 | |
919 console.log(remoting.timestamp(), | |
920 remoting.formatIq.prettifyReceiveIq(stanza)); | |
921 forwardIq(stanza); | |
922 }; | |
923 remoting.wcsSandbox.setOnIq(onIncomingIq); | |
924 } | |
925 | |
926 /** | |
927 * Gets shared secret to be used for connection. | 899 * Gets shared secret to be used for connection. |
928 * | 900 * |
929 * @param {function(string)} callback Callback called with the shared secret. | 901 * @param {function(string)} callback Callback called with the shared secret. |
930 * @return {void} Nothing. | 902 * @return {void} Nothing. |
931 * @private | 903 * @private |
932 */ | 904 */ |
933 remoting.ClientSession.prototype.getSharedSecret_ = function(callback) { | 905 remoting.ClientSession.prototype.getSharedSecret_ = function(callback) { |
934 /** @type remoting.ClientSession */ | 906 /** @type remoting.ClientSession */ |
935 var that = this; | 907 var that = this; |
936 if (this.plugin_.hasFeature(remoting.ClientPlugin.Feature.THIRD_PARTY_AUTH)) { | 908 if (this.plugin_.hasFeature(remoting.ClientPlugin.Feature.THIRD_PARTY_AUTH)) { |
(...skipping 700 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
1637 * @param {string} data Contents of the extension message. | 1609 * @param {string} data Contents of the extension message. |
1638 * @return {boolean} True if the message was recognized, false otherwise. | 1610 * @return {boolean} True if the message was recognized, false otherwise. |
1639 */ | 1611 */ |
1640 remoting.ClientSession.prototype.handleExtensionMessage = | 1612 remoting.ClientSession.prototype.handleExtensionMessage = |
1641 function(type, data) { | 1613 function(type, data) { |
1642 if (this.videoFrameRecorder_) { | 1614 if (this.videoFrameRecorder_) { |
1643 return this.videoFrameRecorder_.handleMessage(type, data); | 1615 return this.videoFrameRecorder_.handleMessage(type, data); |
1644 } | 1616 } |
1645 return false; | 1617 return false; |
1646 } | 1618 } |
OLD | NEW |