Chromium Code Reviews| Index: remoting/webapp/session_connector.js |
| diff --git a/remoting/webapp/session_connector.js b/remoting/webapp/session_connector.js |
| index 04fe425e21634d38e4dd7579a1ed8f8d9f8c30f5..dfdcda6eb1eac0cf40003cb5f268a921b43610e9 100644 |
| --- a/remoting/webapp/session_connector.js |
| +++ b/remoting/webapp/session_connector.js |
| @@ -1,10 +1,10 @@ |
| -// Copyright 2013 The Chromium Authors. All rights reserved. |
| +// Copyright 2014 The Chromium Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| /** |
| * @fileoverview |
| - * Connect set-up state machine for Me2Me and IT2Me |
| + * 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.
|
| */ |
| 'use strict'; |
| @@ -13,179 +13,15 @@ |
| var remoting = remoting || {}; |
| /** |
| - * @param {HTMLElement} clientContainer Container element for the client view. |
| - * @param {function(remoting.ClientSession):void} onConnected Callback on |
| - * success. |
| - * @param {function(remoting.Error):void} onError Callback on error. |
| - * @param {function(string, string):boolean} onExtensionMessage The handler for |
| - * protocol extension messages. Returns true if a message is recognized; |
| - * false otherwise. |
| - * @constructor |
| + * @interface |
| */ |
| -remoting.SessionConnector = function(clientContainer, onConnected, onError, |
| - onExtensionMessage) { |
| - /** |
| - * @type {HTMLElement} |
| - * @private |
| - */ |
| - this.clientContainer_ = clientContainer; |
| - |
| - /** |
| - * @type {function(remoting.ClientSession):void} |
| - * @private |
| - */ |
| - this.onConnected_ = onConnected; |
| - |
| - /** |
| - * @type {function(remoting.Error):void} |
| - * @private |
| - */ |
| - this.onError_ = onError; |
| - |
| - /** |
| - * @type {function(string, string):boolean} |
| - * @private |
| - */ |
| - this.onExtensionMessage_ = onExtensionMessage; |
| - |
| - /** |
| - * @type {string} |
| - * @private |
| - */ |
| - this.clientJid_ = ''; |
| - |
| - /** |
| - * @type {remoting.ClientSession.Mode} |
| - * @private |
| - */ |
| - this.connectionMode_ = remoting.ClientSession.Mode.ME2ME; |
| - |
| - /** |
| - * @type {remoting.SignalStrategy} |
| - * @private |
| - */ |
| - this.signalStrategy_ = null; |
| - |
| - /** |
| - * @type {remoting.SmartReconnector} |
| - * @private |
| - */ |
| - this.reconnector_ = null; |
| - |
| - /** |
| - * @private |
| - */ |
| - this.bound_ = { |
| - onStateChange : this.onStateChange_.bind(this) |
| - }; |
| - |
| - // Initialize/declare per-connection state. |
| - this.reset(); |
| -}; |
| +remoting.SessionConnector = function() {}; |
| /** |
| * Reset the per-connection state so that the object can be re-used for a |
| * second connection. Note the none of the shared WCS state is reset. |
| */ |
| -remoting.SessionConnector.prototype.reset = function() { |
| - /** |
| - * Set to true to indicate that the user requested pairing when entering |
| - * their PIN for a Me2Me connection. |
| - * |
| - * @type {boolean} |
| - */ |
| - this.pairingRequested = false; |
| - |
| - /** |
| - * String used to identify the host to which to connect. For IT2Me, this is |
| - * the first 7 digits of the access code; for Me2Me it is the host identifier. |
| - * |
| - * @type {string} |
| - * @private |
| - */ |
| - this.hostId_ = ''; |
| - |
| - /** |
| - * For paired connections, the client id of this device, issued by the host. |
| - * |
| - * @type {string} |
| - * @private |
| - */ |
| - this.clientPairingId_ = ''; |
| - |
| - /** |
| - * For paired connections, the paired secret for this device, issued by the |
| - * host. |
| - * |
| - * @type {string} |
| - * @private |
| - */ |
| - this.clientPairedSecret_ = ''; |
| - |
| - /** |
| - * String used to authenticate to the host on connection. For IT2Me, this is |
| - * the access code; for Me2Me it is the PIN. |
| - * |
| - * @type {string} |
| - * @private |
| - */ |
| - this.passPhrase_ = ''; |
| - |
| - /** |
| - * @type {string} |
| - * @private |
| - */ |
| - this.hostJid_ = ''; |
| - |
| - /** |
| - * @type {string} |
| - * @private |
| - */ |
| - this.hostPublicKey_ = ''; |
| - |
| - /** |
| - * @type {boolean} |
| - * @private |
| - */ |
| - this.refreshHostJidIfOffline_ = false; |
| - |
| - /** |
| - * @type {remoting.ClientSession} |
| - * @private |
| - */ |
| - this.clientSession_ = null; |
| - |
| - /** |
| - * @type {XMLHttpRequest} |
| - * @private |
| - */ |
| - this.pendingXhr_ = null; |
| - |
| - /** |
| - * Function to interactively obtain the PIN from the user. |
| - * @type {function(boolean, function(string):void):void} |
| - * @private |
| - */ |
| - this.fetchPin_ = function(onPinFetched) {}; |
| - |
| - /** |
| - * @type {function(string, string, string, |
| - * function(string, string):void): void} |
| - * @private |
| - */ |
| - this.fetchThirdPartyToken_ = function( |
| - tokenUrl, scope, onThirdPartyTokenFetched) {}; |
| - |
| - /** |
| - * Host 'name', as displayed in the client tool-bar. For a Me2Me connection, |
| - * this is the name of the host; for an IT2Me connection, it is the email |
| - * address of the person sharing their computer. |
| - * |
| - * @type {string} |
| - * @private |
| - */ |
| - this.hostDisplayName_ = ''; |
| -}; |
| +remoting.SessionConnector.prototype.reset = function() {}; |
| /** |
| * Initiate a Me2Me connection. |
| @@ -205,12 +41,7 @@ remoting.SessionConnector.prototype.reset = function() { |
| */ |
| remoting.SessionConnector.prototype.connectMe2Me = |
| function(host, fetchPin, fetchThirdPartyToken, |
| - clientPairingId, clientPairedSecret) { |
| - this.connectMe2MeInternal_( |
| - host.hostId, host.jabberId, host.publicKey, host.hostName, |
| - fetchPin, fetchThirdPartyToken, |
| - clientPairingId, clientPairedSecret, true); |
| -}; |
| + clientPairingId, clientPairedSecret) {}; |
| /** |
| * Update the pairing info so that the reconnect function will work correctly. |
| @@ -219,53 +50,7 @@ remoting.SessionConnector.prototype.connectMe2Me = |
| * @param {string} sharedSecret The shared secret. |
| */ |
| remoting.SessionConnector.prototype.updatePairingInfo = |
| - function(clientId, sharedSecret) { |
| - this.clientPairingId_ = clientId; |
| - this.clientPairedSecret_ = sharedSecret; |
| -}; |
| - |
| -/** |
| - * Initiate a Me2Me connection. |
| - * |
| - * @param {string} hostId ID of the Me2Me host. |
| - * @param {string} hostJid XMPP JID of the host. |
| - * @param {string} hostPublicKey Public Key of the host. |
| - * @param {string} hostDisplayName Display name (friendly name) of the host. |
| - * @param {function(boolean, function(string):void):void} fetchPin Function to |
| - * interactively obtain the PIN from the user. |
| - * @param {function(string, string, string, |
| - * function(string, string): void): void} |
| - * fetchThirdPartyToken Function to obtain a token from a third party |
| - * authenticaiton server. |
| - * @param {string} clientPairingId The client id issued by the host when |
| - * this device was paired, if it is already paired. |
| - * @param {string} clientPairedSecret The shared secret issued by the host when |
| - * this device was paired, if it is already paired. |
| - * @param {boolean} refreshHostJidIfOffline Whether to refresh the JID and retry |
| - * the connection if the current JID is offline. |
| - * @return {void} Nothing. |
| - * @private |
| - */ |
| -remoting.SessionConnector.prototype.connectMe2MeInternal_ = |
| - function(hostId, hostJid, hostPublicKey, hostDisplayName, |
| - fetchPin, fetchThirdPartyToken, |
| - clientPairingId, clientPairedSecret, |
| - refreshHostJidIfOffline) { |
| - // Cancel any existing connect operation. |
| - this.cancel(); |
| - |
| - this.hostId_ = hostId; |
| - this.hostJid_ = hostJid; |
| - this.hostPublicKey_ = hostPublicKey; |
| - this.fetchPin_ = fetchPin; |
| - this.fetchThirdPartyToken_ = fetchThirdPartyToken; |
| - this.hostDisplayName_ = hostDisplayName; |
| - this.connectionMode_ = remoting.ClientSession.Mode.ME2ME; |
| - this.refreshHostJidIfOffline_ = refreshHostJidIfOffline; |
| - this.updatePairingInfo(clientPairingId, clientPairedSecret); |
| - |
| - this.connectSignaling_(); |
| -} |
| + function(clientId, sharedSecret) {}; |
| /** |
| * Initiate an IT2Me connection. |
| @@ -273,322 +58,55 @@ remoting.SessionConnector.prototype.connectMe2MeInternal_ = |
| * @param {string} accessCode The access code as entered by the user. |
| * @return {void} Nothing. |
| */ |
| -remoting.SessionConnector.prototype.connectIT2Me = function(accessCode) { |
| - var kSupportIdLen = 7; |
| - var kHostSecretLen = 5; |
| - var kAccessCodeLen = kSupportIdLen + kHostSecretLen; |
| - |
| - // Cancel any existing connect operation. |
| - this.cancel(); |
| - |
| - var normalizedAccessCode = this.normalizeAccessCode_(accessCode); |
| - if (normalizedAccessCode.length != kAccessCodeLen) { |
| - this.onError_(remoting.Error.INVALID_ACCESS_CODE); |
| - return; |
| - } |
| - |
| - this.hostId_ = normalizedAccessCode.substring(0, kSupportIdLen); |
| - this.passPhrase_ = normalizedAccessCode; |
| - this.connectionMode_ = remoting.ClientSession.Mode.IT2ME; |
| - remoting.identity.callWithToken(this.connectIT2MeWithToken_.bind(this), |
| - this.onError_); |
| -}; |
| +remoting.SessionConnector.prototype.connectIT2Me = |
| + function(accessCode) {}; |
| /** |
| * Reconnect a closed connection. |
| * |
| * @return {void} Nothing. |
| */ |
| -remoting.SessionConnector.prototype.reconnect = function() { |
| - if (this.connectionMode_ == remoting.ClientSession.Mode.IT2ME) { |
| - console.error('reconnect not supported for IT2Me.'); |
| - return; |
| - } |
| - this.connectMe2MeInternal_( |
| - this.hostId_, this.hostJid_, this.hostPublicKey_, this.hostDisplayName_, |
| - this.fetchPin_, this.fetchThirdPartyToken_, |
| - this.clientPairingId_, this.clientPairedSecret_, true); |
| -}; |
| +remoting.SessionConnector.prototype.reconnect = function() {}; |
| /** |
| * Cancel a connection-in-progress. |
| */ |
| -remoting.SessionConnector.prototype.cancel = function() { |
| - if (this.clientSession_) { |
| - this.clientSession_.removePlugin(); |
| - this.clientSession_ = null; |
| - } |
| - if (this.pendingXhr_) { |
| - this.pendingXhr_.abort(); |
| - this.pendingXhr_ = null; |
| - } |
| - this.reset(); |
| -}; |
| +remoting.SessionConnector.prototype.cancel = function() {}; |
| /** |
| * Get the connection mode (Me2Me or IT2Me) |
| * |
| * @return {remoting.ClientSession.Mode} |
| */ |
| -remoting.SessionConnector.prototype.getConnectionMode = function() { |
| - return this.connectionMode_; |
| -}; |
| +remoting.SessionConnector.prototype.getConnectionMode = function() {}; |
| /** |
| * Get host ID. |
| * |
| * @return {string} |
| */ |
| -remoting.SessionConnector.prototype.getHostId = function() { |
| - return this.hostId_; |
| -}; |
| - |
| -/** |
| - * @private |
| - */ |
| -remoting.SessionConnector.prototype.connectSignaling_ = function() { |
| - base.dispose(this.signalStrategy_); |
| - this.signalStrategy_ = null; |
| - |
| - /** @type {remoting.SessionConnector} */ |
| - var that = this; |
| - |
| - /** @param {string} token */ |
| - function connectSignalingWithToken(token) { |
| - remoting.identity.getEmail( |
| - connectSignalingWithTokenAndEmail.bind(null, token), that.onError_); |
| - } |
| - |
| - /** |
| - * @param {string} token |
| - * @param {string} email |
| - */ |
| - function connectSignalingWithTokenAndEmail(token, email) { |
| - that.signalStrategy_.connect( |
| - remoting.settings.XMPP_SERVER_ADDRESS, email, token); |
| - } |
| - |
| - // Only use XMPP when TCP API is available and TLS support is enabled. That's |
| - // not the case for V1 app (socket API is available only to platform apps) |
| - // and for Chrome releases before 38. |
| - if (chrome.socket && chrome.socket.secure) { |
| - this.signalStrategy_ = /** @type {remoting.SignalStrategy} */ |
| - (new remoting.XmppConnection(this.onSignalingState_.bind(this))); |
| - } else { |
| - this.signalStrategy_ = /** @type {remoting.SignalStrategy} */ |
| - (new remoting.WcsAdapter(this.onSignalingState_.bind(this))); |
| - } |
| - |
| - remoting.identity.callWithToken(connectSignalingWithToken, this.onError_); |
| -}; |
| - |
| -/** |
| - * @private |
| - * @param {remoting.SignalStrategy.State} state |
| - */ |
| -remoting.SessionConnector.prototype.onSignalingState_ = function(state) { |
| - switch (state) { |
| - case remoting.SignalStrategy.State.CONNECTED: |
| - // Proceed only if the connection hasn't been canceled. |
| - if (this.hostJid_) { |
| - this.createSession_(); |
| - } |
| - break; |
| - |
| - case remoting.SignalStrategy.State.FAILED: |
| - this.onError_(this.signalStrategy_.getError()); |
| - break; |
| - } |
| -}; |
| - |
| -/** |
| - * Continue an IT2Me connection once an access token has been obtained. |
| - * |
| - * @param {string} token An OAuth2 access token. |
| - * @return {void} Nothing. |
| - * @private |
| - */ |
| -remoting.SessionConnector.prototype.connectIT2MeWithToken_ = function(token) { |
| - // Resolve the host id to get the host JID. |
| - this.pendingXhr_ = remoting.xhr.get( |
| - remoting.settings.DIRECTORY_API_BASE_URL + '/support-hosts/' + |
| - encodeURIComponent(this.hostId_), |
| - this.onIT2MeHostInfo_.bind(this), |
| - '', |
| - { 'Authorization': 'OAuth ' + token }); |
| -}; |
| +remoting.SessionConnector.prototype.getHostId = function() {}; |
| -/** |
| - * Continue an IT2Me connection once the host JID has been looked up. |
| - * |
| - * @param {XMLHttpRequest} xhr The server response to the support-hosts query. |
| - * @return {void} Nothing. |
| - * @private |
| - */ |
| -remoting.SessionConnector.prototype.onIT2MeHostInfo_ = function(xhr) { |
| - this.pendingXhr_ = null; |
| - if (xhr.status == 200) { |
| - var host = /** @type {{data: {jabberId: string, publicKey: string}}} */ |
| - jsonParseSafe(xhr.responseText); |
| - if (host && host.data && host.data.jabberId && host.data.publicKey) { |
| - this.hostJid_ = host.data.jabberId; |
| - this.hostPublicKey_ = host.data.publicKey; |
| - this.hostDisplayName_ = this.hostJid_.split('/')[0]; |
| - this.createSession_(); |
| - return; |
| - } else { |
| - console.error('Invalid "support-hosts" response from server.'); |
| - } |
| - } else { |
| - this.onError_(this.translateSupportHostsError(xhr.status)); |
| - } |
| -}; |
| /** |
| - * Creates ClientSession object. |
| + * @interface |
| */ |
| -remoting.SessionConnector.prototype.createSession_ = function() { |
| - // In some circumstances, the WCS <iframe> can get reloaded, which results |
| - // in a new clientJid and a new callback. In this case, remove the old |
| - // client plugin before instantiating a new one. |
| - if (this.clientSession_) { |
| - this.clientSession_.removePlugin(); |
| - this.clientSession_ = null; |
| - } |
| - |
| - var authenticationMethods = |
| - 'third_party,spake2_pair,spake2_hmac,spake2_plain'; |
| - this.clientSession_ = new remoting.ClientSession( |
| - this.signalStrategy_, this.clientContainer_, this.hostDisplayName_, |
| - this.passPhrase_, this.fetchPin_, this.fetchThirdPartyToken_, |
| - authenticationMethods, this.hostId_, this.hostJid_, this.hostPublicKey_, |
| - this.connectionMode_, this.clientPairingId_, this.clientPairedSecret_); |
| - this.clientSession_.logHostOfflineErrors(!this.refreshHostJidIfOffline_); |
| - this.clientSession_.addEventListener( |
| - remoting.ClientSession.Events.stateChanged, |
| - this.bound_.onStateChange); |
| - this.clientSession_.createPluginAndConnect(this.onExtensionMessage_); |
| -}; |
| +remoting.SessionConnectorFactory = function() {}; |
| /** |
| - * Handle a change in the state of the client session prior to successful |
| - * connection (after connection, this class no longer handles state change |
| - * events). Errors that occur while connecting either trigger a reconnect |
| - * or notify the onError handler. |
| - * |
| - * @param {remoting.ClientSession.StateEvent} event |
| - * @return {void} Nothing. |
| - * @private |
| - */ |
| -remoting.SessionConnector.prototype.onStateChange_ = function(event) { |
| - switch (event.current) { |
| - case remoting.ClientSession.State.CONNECTED: |
| - // When the connection succeeds, deregister for state-change callbacks |
| - // and pass the session to the onConnected callback. It is expected that |
| - // it will register a new state-change callback to handle disconnect |
| - // or error conditions. |
| - this.clientSession_.removeEventListener( |
| - remoting.ClientSession.Events.stateChanged, |
| - this.bound_.onStateChange); |
| - |
| - base.dispose(this.reconnector_); |
| - if (this.connectionMode_ != remoting.ClientSession.Mode.IT2ME) { |
| - this.reconnector_ = |
| - new remoting.SmartReconnector(this, this.clientSession_); |
| - } |
| - this.onConnected_(this.clientSession_); |
| - break; |
| - |
| - case remoting.ClientSession.State.CREATED: |
| - console.log('Created plugin'); |
| - break; |
| - |
| - case remoting.ClientSession.State.CONNECTING: |
| - console.log('Connecting as ' + remoting.identity.getCachedEmail()); |
| - break; |
| - |
| - case remoting.ClientSession.State.INITIALIZING: |
| - console.log('Initializing connection'); |
| - break; |
| - |
| - case remoting.ClientSession.State.CLOSED: |
| - // This class deregisters for state-change callbacks when the CONNECTED |
| - // state is reached, so it only sees the CLOSED state in exceptional |
| - // circumstances. For example, a CONNECTING -> CLOSED transition happens |
| - // if the host closes the connection without an error message instead of |
| - // accepting it. Since there's no way of knowing exactly what went wrong, |
| - // we rely on server-side logs in this case and report a generic error |
| - // message. |
| - this.onError_(remoting.Error.UNEXPECTED); |
| - break; |
| - |
| - case remoting.ClientSession.State.FAILED: |
| - var error = this.clientSession_.getError(); |
| - console.error('Client plugin reported connection failed: ' + error); |
| - if (error == null) { |
| - error = remoting.Error.UNEXPECTED; |
| - } |
| - if (error == remoting.Error.HOST_IS_OFFLINE && |
| - this.refreshHostJidIfOffline_) { |
| - // The plugin will be re-created when the host finished refreshing |
| - remoting.hostList.refresh(this.onHostListRefresh_.bind(this)); |
| - } else { |
| - this.onError_(error); |
| - } |
| - break; |
| - |
| - default: |
| - console.error('Unexpected client plugin state: ' + event.current); |
| - // This should only happen if the web-app and client plugin get out of |
| - // sync, and even then the version check should ensure compatibility. |
| - this.onError_(remoting.Error.MISSING_PLUGIN); |
| - } |
| -}; |
| - |
| -/** |
| - * @param {boolean} success True if the host list was successfully refreshed; |
| - * false if an error occurred. |
| - * @private |
| - */ |
| -remoting.SessionConnector.prototype.onHostListRefresh_ = function(success) { |
| - if (success) { |
| - var host = remoting.hostList.getHostForId(this.hostId_); |
| - if (host) { |
| - this.connectMe2MeInternal_( |
| - host.hostId, host.jabberId, host.publicKey, host.hostName, |
| - this.fetchPin_, this.fetchThirdPartyToken_, |
| - this.clientPairingId_, this.clientPairedSecret_, false); |
| - return; |
| - } |
| - } |
| - this.onError_(remoting.Error.HOST_IS_OFFLINE); |
| -}; |
| - |
| -/** |
| - * @param {number} error An HTTP error code returned by the support-hosts |
| - * endpoint. |
| - * @return {remoting.Error} The equivalent remoting.Error code. |
| - * @private |
| + * @param {HTMLElement} clientContainer Container element for the client view. |
| + * @param {function(remoting.ClientSession):void} onConnected Callback on |
| + * success. |
| + * @param {function(remoting.Error):void} onError Callback on error. |
| + * @param {function(string, string):boolean} onExtensionMessage The handler for |
| + * protocol extension messages. Returns true if a message is recognized; |
| + * false otherwise. |
| + * @return {remoting.SessionConnector} |
| */ |
| -remoting.SessionConnector.prototype.translateSupportHostsError = |
| - function(error) { |
| - switch (error) { |
| - case 0: return remoting.Error.NETWORK_FAILURE; |
| - case 404: return remoting.Error.INVALID_ACCESS_CODE; |
| - case 502: // No break |
| - case 503: return remoting.Error.SERVICE_UNAVAILABLE; |
| - default: return remoting.Error.UNEXPECTED; |
| - } |
| -}; |
| +remoting.SessionConnectorFactory.prototype.createConnector = |
| + function(clientContainer, onConnected, onError, onExtensionMessage) {}; |
| /** |
| - * Normalize the access code entered by the user. |
| - * |
| - * @param {string} accessCode The access code, as entered by the user. |
| - * @return {string} The normalized form of the code (whitespace removed). |
| + * @type {remoting.SessionConnectorFactory} |
| */ |
| -remoting.SessionConnector.prototype.normalizeAccessCode_ = |
| - function(accessCode) { |
| - // Trim whitespace. |
| - return accessCode.replace(/\s/g, ''); |
| -}; |
| +remoting.SessionConnector.factory = null; |