Chromium Code Reviews| 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 |