diff options
author | kelvinp <kelvinp@chromium.org> | 2015-04-17 17:39:45 -0700 |
---|---|---|
committer | Commit bot <commit-bot@chromium.org> | 2015-04-18 00:40:06 +0000 |
commit | cb655c2ca0a46c9402c65e74ef736037ed6bd3a8 (patch) | |
tree | 54f7fcd1e364cf61b345b565936f33cb449484fa /remoting | |
parent | 9bb592340bc912b5c8d09432b139e04aa8713842 (diff) | |
download | chromium_src-cb655c2ca0a46c9402c65e74ef736037ed6bd3a8.zip chromium_src-cb655c2ca0a46c9402c65e74ef736037ed6bd3a8.tar.gz chromium_src-cb655c2ca0a46c9402c65e74ef736037ed6bd3a8.tar.bz2 |
Revert of [Webapp Refactor] Remove remoting.SessionConnector. (patchset #3 id:60001 of https://codereview.chromium.org/1047413006/)
Reason for revert:
Bad merge with a previous version that will cause compilation failure.
Original issue's description:
> [Webapp Refactor] Remove remoting.SessionConnector.
>
> remoting.SessionConnector is currently responsible for creating
> the plugin, the signal strategy and the clientSession. It also
> magically disposes the plugin when the clientSession finishes.
>
> However, the session is not exposed to the caller elsewhere, which makes
> it hard for the caller to dispose of the session before it is connected,
> e.g. Cancel a PIN entry.
>
> It is currently a stateful object that keeps track of the host,
> credentials provider, strategy of the created session, which is redundant.
>
> This CL
> 1. Offloads the creation of the clientSession to the
> remoting.ClientSessionFactory. It also allow the caller to configure
> a clientSession prior to connecting. The remoting.ClientSessionFactory
> is essentially stateless except for a few predefined ClientSession
> construction parameters.
> 2. Allow the caller to have a different instance of
> ClientSession.EventHandler for each instance ClientSession created.
> 3. Revives remoting.MockClientPlugin and uses it for remoting.ClientSessionFactory
> unittests.
>
> Ownership graph before:
> Activity -> SessionConenctor -> ClientSession
>
> Ownership graph after:
> Activity -> ClientSession
>
> BUG=477522
> TEST=All browser tests passed on
> https://chromium-swarm.appspot.com/user/tasks?sort=created_ts&state=all&limit=10&task_name=chromoting_integration_tests
>
> Committed: https://crrev.com/4c69287fc3e0ad6663ab98f02ef57c570f8992e3
> Cr-Commit-Position: refs/heads/master@{#325745}
TBR=jamiewalch@chromium.org,garykac@chromium.org
NOPRESUBMIT=true
NOTREECHECKS=true
NOTRY=true
BUG=477522
Review URL: https://codereview.chromium.org/1096883002
Cr-Commit-Position: refs/heads/master@{#325748}
Diffstat (limited to 'remoting')
-rw-r--r-- | remoting/remoting_webapp_files.gypi | 6 | ||||
-rw-r--r-- | remoting/webapp/app_remoting/js/app_remoting_activity.js | 24 | ||||
-rw-r--r-- | remoting/webapp/base/js/application.js | 2 | ||||
-rw-r--r-- | remoting/webapp/base/js/protocol_extension.js | 2 | ||||
-rw-r--r-- | remoting/webapp/crd/js/client_session.js | 98 | ||||
-rw-r--r-- | remoting/webapp/crd/js/client_session_factory.js | 134 | ||||
-rw-r--r-- | remoting/webapp/crd/js/client_session_factory_unittest.js | 100 | ||||
-rw-r--r-- | remoting/webapp/crd/js/desktop_remoting_activity.js | 26 | ||||
-rw-r--r-- | remoting/webapp/crd/js/it2me_activity.js | 10 | ||||
-rw-r--r-- | remoting/webapp/crd/js/me2me_activity.js | 11 | ||||
-rw-r--r-- | remoting/webapp/crd/js/mock_client_plugin.js | 82 | ||||
-rw-r--r-- | remoting/webapp/crd/js/mock_session_connector.js | 171 | ||||
-rw-r--r-- | remoting/webapp/crd/js/session_connector.js | 52 | ||||
-rw-r--r-- | remoting/webapp/crd/js/session_connector_impl.js | 291 |
14 files changed, 599 insertions, 410 deletions
diff --git a/remoting/remoting_webapp_files.gypi b/remoting/remoting_webapp_files.gypi index c65ea7f..e327d72 100644 --- a/remoting/remoting_webapp_files.gypi +++ b/remoting/remoting_webapp_files.gypi @@ -39,6 +39,7 @@ 'webapp/crd/js/mock_host_list_api.js', 'webapp/crd/js/mock_identity.js', 'webapp/crd/js/mock_oauth2_api.js', + 'webapp/crd/js/mock_session_connector.js', 'webapp/crd/js/mock_signal_strategy.js', ], 'remoting_webapp_browsertest_js_proto_files': [ @@ -73,7 +74,6 @@ 'webapp/base/js/protocol_extension_manager_unittest.js', 'webapp/crd/js/apps_v2_migration_unittest.js', 'webapp/crd/js/desktop_viewport_unittest.js', - 'webapp/crd/js/client_session_factory_unittest.js', 'webapp/crd/js/dns_blackhole_checker_unittest.js', 'webapp/crd/js/error_unittest.js', 'webapp/crd/js/fallback_signal_strategy_unittest.js', @@ -95,7 +95,6 @@ 'remoting_webapp_unittests_js_mock_files': [ # Some proto files can be repurposed as simple mocks for the unittests. # Note that some defs in chrome_proto are overwritten by chrome_mocks. - 'webapp/crd/js/mock_client_plugin.js', 'webapp/crd/js/mock_host_daemon_facade.js', 'webapp/crd/js/mock_signal_strategy.js', 'webapp/js_proto/chrome_proto.js', @@ -158,13 +157,14 @@ 'webapp/crd/js/client_plugin_impl.js', 'webapp/crd/js/client_plugin_host_desktop_impl.js', 'webapp/crd/js/client_session.js', - 'webapp/crd/js/client_session_factory.js', 'webapp/crd/js/clipboard.js', 'webapp/crd/js/connected_view.js', 'webapp/crd/js/connection_info.js', 'webapp/crd/js/credentials_provider.js', 'webapp/crd/js/desktop_connected_view.js', 'webapp/crd/js/host_desktop.js', + 'webapp/crd/js/session_connector.js', + 'webapp/crd/js/session_connector_impl.js', 'webapp/crd/js/smart_reconnector.js', 'webapp/crd/js/video_frame_recorder.js', ], diff --git a/remoting/webapp/app_remoting/js/app_remoting_activity.js b/remoting/webapp/app_remoting/js/app_remoting_activity.js index fecd55d..3685946 100644 --- a/remoting/webapp/app_remoting/js/app_remoting_activity.js +++ b/remoting/webapp/app_remoting/js/app_remoting_activity.js @@ -35,9 +35,9 @@ remoting.AppRemotingActivity = function(appCapabilities) { this.connectedView_ = null; /** @private */ - this.sessionFactory_ = new remoting.ClientSessionFactory( - document.querySelector('#client-container .client-plugin-container'), - appCapabilities); + this.connector_ = remoting.SessionConnector.factory.createConnector( + document.getElementById('client-container'), appCapabilities, + this); /** @private {remoting.ClientSession} */ this.session_ = null; @@ -69,7 +69,6 @@ remoting.AppRemotingActivity.prototype.stop = function() { remoting.AppRemotingActivity.prototype.cleanup_ = function() { base.dispose(this.connectedView_); this.connectedView_ = null; - base.dispose(this.session_); this.session_ = null; }; @@ -128,17 +127,11 @@ remoting.AppRemotingActivity.prototype.onAppHostResponse_ = host['sharedSecret']); }; - remoting.app.setConnectionMode(remoting.Application.Mode.APP_REMOTING); - var credentialsProvider = new remoting.CredentialsProvider( - {fetchThirdPartyToken: fetchThirdPartyToken}); - var that = this; - - this.sessionFactory_.createSession(this).then( - function(/** remoting.ClientSession */ session) { - that.session_ = session; - session.logHostOfflineErrors(true); - session.connect(host, credentialsProvider); - }); + this.connector_.connect( + remoting.Application.Mode.APP_REMOTING, host, + new remoting.CredentialsProvider( + {fetchThirdPartyToken: fetchThirdPartyToken})); + } else if (response && response.status == 'pending') { this.onError(new remoting.Error( remoting.Error.Tag.SERVICE_UNAVAILABLE)); @@ -156,6 +149,7 @@ remoting.AppRemotingActivity.prototype.onConnected = function(connectionInfo) { this.connectedView_ = new remoting.AppConnectedView( document.getElementById('client-container'), connectionInfo); + this.session_ = connectionInfo.session(); var idleDetector = new remoting.IdleDetector( document.getElementById('idle-dialog'), this.stop.bind(this)); diff --git a/remoting/webapp/base/js/application.js b/remoting/webapp/base/js/application.js index 48a4f61..63cafa0 100644 --- a/remoting/webapp/base/js/application.js +++ b/remoting/webapp/base/js/application.js @@ -25,6 +25,8 @@ remoting.testEvents; remoting.Application = function() { // Create global factories. remoting.ClientPlugin.factory = new remoting.DefaultClientPluginFactory(); + remoting.SessionConnector.factory = + new remoting.DefaultSessionConnectorFactory(); /** @protected {remoting.Application.Mode} */ this.connectionMode_ = remoting.Application.Mode.ME2ME; diff --git a/remoting/webapp/base/js/protocol_extension.js b/remoting/webapp/base/js/protocol_extension.js index 11d8aa6..28103f7 100644 --- a/remoting/webapp/base/js/protocol_extension.js +++ b/remoting/webapp/base/js/protocol_extension.js @@ -5,7 +5,7 @@ /** * @fileoverview * Interface abstracting the protocol extension functionality. - * Instances of this class can be registered with the ProtocolExtensionManager + * Instances of this class can be registered with the SessionConnector * to enhance the communication protocol between the host and client. * Note that corresponding support on the host side is required. */ diff --git a/remoting/webapp/crd/js/client_session.js b/remoting/webapp/crd/js/client_session.js index b31d69f..f76069e 100644 --- a/remoting/webapp/crd/js/client_session.js +++ b/remoting/webapp/crd/js/client_session.js @@ -8,8 +8,9 @@ * * The ClientSession class controls lifetime of the client plugin * object and provides the plugin with the functionality it needs to - * establish connection, e.g. delivers incoming/outgoing signaling - * messages. + * establish connection. Specifically it: + * - Delivers incoming/outgoing signaling messages, + * - Adjusts plugin size and position when destop resolution changes, * * This class should not access the plugin directly, instead it should * do it through ClientPlugin class which abstracts plugin version @@ -23,15 +24,15 @@ var remoting = remoting || {}; /** * @param {remoting.ClientPlugin} plugin + * @param {remoting.Host} host The host to connect to. * @param {remoting.SignalStrategy} signalStrategy Signal strategy. - * @param {remoting.ClientSession.EventHandler} listener * * @constructor * @extends {base.EventSourceImpl} * @implements {base.Disposable} * @implements {remoting.ClientPlugin.ConnectionEventHandler} */ -remoting.ClientSession = function(plugin, signalStrategy, listener) { +remoting.ClientSession = function(plugin, host, signalStrategy) { base.inherits(this, base.EventSourceImpl); /** @private */ @@ -40,17 +41,11 @@ remoting.ClientSession = function(plugin, signalStrategy, listener) { /** @private {!remoting.Error} */ this.error_ = remoting.Error.none(); - /** @private {remoting.Host} */ - this.host_ = null; - - /** @private {remoting.CredentialsProvider} */ - this.credentialsProvider_ = null; - /** @private */ - this.sessionId_ = ''; + this.host_ = host; /** @private */ - this.listener_ = listener; + this.sessionId_ = ''; /** @private */ this.hasReceivedFrame_ = false; @@ -63,8 +58,9 @@ remoting.ClientSession = function(plugin, signalStrategy, listener) { this.signalStrategy_.setIncomingStanzaCallback( this.onIncomingMessage_.bind(this)); - /** @private {remoting.FormatIq} */ - this.iqFormatter_ = null; + /** @private */ + this.iqFormatter_ = + new remoting.FormatIq(this.signalStrategy_.getJid(), host.jabberId); /** * Allow host-offline error reporting to be suppressed in situations where it @@ -259,21 +255,6 @@ remoting.ClientSession.Capability = { }; /** - * Connects to |host| using |credentialsProvider| as the credentails. - * - * @param {remoting.Host} host - * @param {remoting.CredentialsProvider} credentialsProvider - */ -remoting.ClientSession.prototype.connect = function(host, credentialsProvider) { - this.host_ = host; - this.credentialsProvider_ = credentialsProvider; - this.iqFormatter_ = - new remoting.FormatIq(this.signalStrategy_.getJid(), host.jabberId); - this.plugin_.connect(this.host_, this.signalStrategy_.getJid(), - credentialsProvider); -}; - -/** * Disconnect the current session with a particular |error|. The session will * raise a |stateChanged| event in response to it. The caller should then call * dispose() to remove and destroy the <embed> element. @@ -316,7 +297,6 @@ remoting.ClientSession.prototype.disconnect = function(error) { remoting.ClientSession.prototype.dispose = function() { base.dispose(this.connectedDisposables_); this.connectedDisposables_ = null; - base.dispose(this.plugin_); this.plugin_ = null; }; @@ -508,65 +488,7 @@ remoting.ClientSession.prototype.setState_ = function(newState) { this.connectedDisposables_ = null; } - this.notifyStateChanges_(oldState, this.state_); this.logToServer.logClientSessionStateChange(this.state_, this.error_); -}; - -/** - * @param {remoting.ClientSession.State} oldState The new state for the session. - * @param {remoting.ClientSession.State} newState The new state for the session. - * @private - */ -remoting.ClientSession.prototype.notifyStateChanges_ = - function(oldState, newState) { - /** @type {remoting.Error} */ - var error; - switch (this.state_) { - case remoting.ClientSession.State.CONNECTED: - console.log('Connection established.'); - var connectionInfo = new remoting.ConnectionInfo( - this.host_, this.credentialsProvider_, this, this.plugin_); - this.listener_.onConnected(connectionInfo); - break; - - case remoting.ClientSession.State.CONNECTING: - remoting.identity.getEmail().then(function(/** string */ email) { - console.log('Connecting as ' + email); - }); - break; - - case remoting.ClientSession.State.AUTHENTICATED: - console.log('Connection authenticated.'); - break; - - case remoting.ClientSession.State.INITIALIZING: - console.log('Connection initializing .'); - break; - - case remoting.ClientSession.State.CLOSED: - console.log('Connection closed.'); - this.listener_.onDisconnected(); - break; - - case remoting.ClientSession.State.FAILED: - error = this.getError(); - console.error('Connection failed: ' + error.toString()); - this.listener_.onConnectionFailed(error); - break; - - case remoting.ClientSession.State.CONNECTION_DROPPED: - error = this.getError(); - console.error('Connection dropped: ' + error.toString()); - this.listener_.onError(error); - break; - - default: - console.error('Unexpected client plugin state: ' + newState); - // 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.listener_.onError( - new remoting.Error(remoting.Error.Tag.MISSING_PLUGIN)); - } this.raiseEvent(remoting.ClientSession.Events.stateChanged, new remoting.ClientSession.StateEvent(newState, oldState) diff --git a/remoting/webapp/crd/js/client_session_factory.js b/remoting/webapp/crd/js/client_session_factory.js deleted file mode 100644 index 24cfa2a..0000000 --- a/remoting/webapp/crd/js/client_session_factory.js +++ /dev/null @@ -1,134 +0,0 @@ -// Copyright 2015 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. - -/** @suppress {duplicate} */ -var remoting = remoting || {}; - -(function() { - -'use strict'; - -/** - * @param {Element} container parent element for the plugin to be created. - * @param {Array<string>} capabilities capabilities required by this - * application. - * @constructor - */ -remoting.ClientSessionFactory = function(container, capabilities) { - /** @private */ - this.container_ = /** @type {HTMLElement} */ (container); - - /** @private {Array<string>} */ - this.requiredCapabilities_ = [ - remoting.ClientSession.Capability.SEND_INITIAL_RESOLUTION, - remoting.ClientSession.Capability.RATE_LIMIT_RESIZE_REQUESTS, - remoting.ClientSession.Capability.VIDEO_RECORDER - ]; - - // Append the app-specific capabilities. - this.requiredCapabilities_.push.apply(this.requiredCapabilities_, - capabilities); -}; - -/** - * Creates a session. - * - * @param {remoting.ClientSession.EventHandler} listener - * @return {Promise<!remoting.ClientSession>} Resolves with the client session - * if succeeded or rejects with remoting.Error on failure. - */ -remoting.ClientSessionFactory.prototype.createSession = function(listener) { - var that = this; - /** @type {string} */ - var token; - /** @type {remoting.SignalStrategy} */ - var signalStrategy; - /** @type {remoting.ClientPlugin} */ - var clientPlugin; - - function OnError(/** remoting.Error */ error) { - base.dispose(signalStrategy); - base.dispose(clientPlugin); - throw error; - } - - var promise = remoting.identity.getToken().then( - function(/** string */ authToken) { - token = authToken; - return remoting.identity.getUserInfo(); - }).then(function(/** {email: string, name: string} */ userInfo) { - return connectSignaling(userInfo.email, token); - }).then(function(/** remoting.SignalStrategy */ strategy) { - signalStrategy = strategy; - return createPlugin(that.container_, that.requiredCapabilities_); - }).then(function(/** remoting.ClientPlugin */ plugin) { - clientPlugin = plugin; - return new remoting.ClientSession(plugin, signalStrategy, listener); - }).catch( - remoting.Error.handler(OnError) - ); - - return /** @type {Promise<!remoting.ClientSession>} */ (promise); -}; - -/** - * @param {string} email - * @param {string} token - * @return {Promise<!remoting.SignalStrategy>} - */ -function connectSignaling(email, token) { - var signalStrategy = remoting.SignalStrategy.create(); - var deferred = new base.Deferred(); - function onSignalingState(/** remoting.SignalStrategy.State */ state) { - switch (state) { - case remoting.SignalStrategy.State.CONNECTED: - deferred.resolve(signalStrategy); - break; - - case remoting.SignalStrategy.State.FAILED: - var error = signalStrategy.getError(); - signalStrategy.dispose(); - deferred.reject(error); - break; - } - } - signalStrategy.setStateChangedCallback(onSignalingState); - signalStrategy.connect(remoting.settings.XMPP_SERVER, email, token); - return deferred.promise(); -} - -/** - * Creates the plugin. - * @param {HTMLElement} container parent element for the plugin. - * @param {Array<string>} capabilities capabilities required by this - * application. - * @return {Promise<!remoting.ClientPlugin>} - */ -function createPlugin(container, capabilities) { - var plugin = remoting.ClientPlugin.factory.createPlugin( - container, capabilities); - var deferred = new base.Deferred(); - - function onInitialized(/** boolean */ initialized) { - if (!initialized) { - console.error('ERROR: remoting plugin not loaded'); - plugin.dispose(); - deferred.reject(new remoting.Error(remoting.Error.Tag.MISSING_PLUGIN)); - return; - } - - if (!plugin.isSupportedVersion()) { - console.error('ERROR: bad plugin version'); - plugin.dispose(); - deferred.reject( - new remoting.Error(remoting.Error.Tag.BAD_PLUGIN_VERSION)); - return; - } - deferred.resolve(plugin); - } - plugin.initialize(onInitialized); - return deferred.promise(); -} - -})(); diff --git a/remoting/webapp/crd/js/client_session_factory_unittest.js b/remoting/webapp/crd/js/client_session_factory_unittest.js deleted file mode 100644 index f0e884c..0000000 --- a/remoting/webapp/crd/js/client_session_factory_unittest.js +++ /dev/null @@ -1,100 +0,0 @@ -// Copyright 2015 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. - -(function() { - -'use strict'; - -var originalPluginFactory; -var originalIdentity; -var originalSettings; - -/** @type {remoting.MockSignalStrategy} */ -var mockSignalStrategy; -/** @type {remoting.ClientSessionFactory} */ -var factory; -/** @type {remoting.ClientSession.EventHandler} */ -var listener; -/** @type {sinon.TestStub} */ -var createSignalStrategyStub; - -/** - * @constructor - * @implements {remoting.ClientSession.EventHandler} - */ -var SessionListener = function() {}; -SessionListener.prototype.onConnectionFailed = function(error) {}; -SessionListener.prototype.onConnected = function(connectionInfo) {}; -SessionListener.prototype.onDisconnected = function() {}; -SessionListener.prototype.onError = function(error) {}; - -QUnit.module('ClientSessionFactory', { - beforeEach: function() { - originalPluginFactory = remoting.ClientPlugin.factory; - remoting.ClientPlugin.factory = new remoting.MockClientPluginFactory(); - - mockSignalStrategy = new remoting.MockSignalStrategy( - 'jid', remoting.SignalStrategy.Type.XMPP); - createSignalStrategyStub = sinon.stub(remoting.SignalStrategy, 'create'); - createSignalStrategyStub.returns(mockSignalStrategy); - listener = new SessionListener(); - - originalIdentity = remoting.identity; - remoting.identity = new remoting.Identity(); - chromeMocks.activate(['identity']); - chromeMocks.identity.mock$setToken('fake_token'); - - originalSettings = remoting.settings; - remoting.settings = new remoting.Settings(); - - remoting.identity.getUserInfo = function() { - return { email: 'email', userName: 'userName'}; - }; - - factory = new remoting.ClientSessionFactory( - document.createElement('div'), ['fake_capability']); - }, - afterEach: function() { - remoting.settings = originalSettings; - remoting.identity = originalIdentity; - chromeMocks.restore(); - remoting.identity = null; - remoting.ClientPlugin.factory = originalPluginFactory; - createSignalStrategyStub.restore(); - } -}); - -QUnit.test('createSession() should return a remoting.ClientSession', - function(assert) { - - mockSignalStrategy.connect = function() { - mockSignalStrategy.setStateForTesting( - remoting.SignalStrategy.State.CONNECTED); - }; - - return factory.createSession(listener).then( - function(/** remoting.ClientSession */ session){ - assert.ok(session instanceof remoting.ClientSession); - }); -}); - -QUnit.test('createSession() should reject on signal strategy failure', - function(assert) { - - mockSignalStrategy.connect = function() { - mockSignalStrategy.setStateForTesting(remoting.SignalStrategy.State.FAILED); - }; - - var signalStrategyDispose = - /** @type {sinon.Spy} */ (sinon.spy(mockSignalStrategy, 'dispose')); - - return factory.createSession(listener).then( - assert.ok.bind(assert, false, 'Expect createSession to fail') - ).catch(function(/** remoting.Error */ error) { - assert.ok(signalStrategyDispose.called); - assert.equal(error.getDetail(), 'setStateForTesting'); - }); -}); - -})(); diff --git a/remoting/webapp/crd/js/desktop_remoting_activity.js b/remoting/webapp/crd/js/desktop_remoting_activity.js index 1e64621..369b228 100644 --- a/remoting/webapp/crd/js/desktop_remoting_activity.js +++ b/remoting/webapp/crd/js/desktop_remoting_activity.js @@ -24,33 +24,10 @@ remoting.DesktopRemotingActivity = function(parentActivity) { this.parentActivity_ = parentActivity; /** @private {remoting.DesktopConnectedView} */ this.connectedView_ = null; - /** @private */ - this.sessionFactory_ = new remoting.ClientSessionFactory( - document.querySelector('#client-container .client-plugin-container'), - remoting.app_capabilities()); /** @private {remoting.ClientSession} */ this.session_ = null; }; -/** - * Initiates a connection. - * - * @param {remoting.Host} host the Host to connect to. - * @param {remoting.CredentialsProvider} credentialsProvider - * @param {boolean=} opt_suppressOfflineError - * @return {void} Nothing. - */ -remoting.DesktopRemotingActivity.prototype.start = - function(host, credentialsProvider, opt_suppressOfflineError) { - var that = this; - this.sessionFactory_.createSession(this).then( - function(/** remoting.ClientSession */ session) { - that.session_ = session; - session.logHostOfflineErrors(!opt_suppressOfflineError); - session.connect(host, credentialsProvider); - }); -}; - remoting.DesktopRemotingActivity.prototype.stop = function() { if (this.session_) { this.session_.disconnect(remoting.Error.none()); @@ -69,6 +46,8 @@ remoting.DesktopRemotingActivity.prototype.onConnected = remoting.toolbar.preview(); } + this.session_ = connectionInfo.session(); + this.connectedView_ = new remoting.DesktopConnectedView( document.getElementById('client-container'), connectionInfo); @@ -121,7 +100,6 @@ remoting.DesktopRemotingActivity.prototype.onError = function(error) { remoting.DesktopRemotingActivity.prototype.dispose = function() { base.dispose(this.connectedView_); this.connectedView_ = null; - base.dispose(this.session_); this.session_ = null; }; diff --git a/remoting/webapp/crd/js/it2me_activity.js b/remoting/webapp/crd/js/it2me_activity.js index e5dc2db..4ee50c3 100644 --- a/remoting/webapp/crd/js/it2me_activity.js +++ b/remoting/webapp/crd/js/it2me_activity.js @@ -185,9 +185,13 @@ remoting.It2MeActivity.prototype.onHostInfo_ = function(xhrResponse) { remoting.It2MeActivity.prototype.connect_ = function(host) { base.dispose(this.desktopActivity_); this.desktopActivity_ = new remoting.DesktopRemotingActivity(this); - remoting.app.setConnectionMode(remoting.Application.Mode.IT2ME); - this.desktopActivity_.start( - host, new remoting.CredentialsProvider({ accessCode: this.passCode_ })); + var sessionConnector = remoting.SessionConnector.factory.createConnector( + document.getElementById('client-container'), + remoting.app_capabilities(), + this.desktopActivity_); + sessionConnector.connect( + remoting.Application.Mode.IT2ME, host, + new remoting.CredentialsProvider({ accessCode: this.passCode_ })); }; /** diff --git a/remoting/webapp/crd/js/me2me_activity.js b/remoting/webapp/crd/js/me2me_activity.js index 7d2b090..7f91246 100644 --- a/remoting/webapp/crd/js/me2me_activity.js +++ b/remoting/webapp/crd/js/me2me_activity.js @@ -72,9 +72,14 @@ remoting.Me2MeActivity.prototype.connect_ = function(suppressHostOfflineError) { remoting.setMode(remoting.AppMode.CLIENT_CONNECTING); base.dispose(this.desktopActivity_); this.desktopActivity_ = new remoting.DesktopRemotingActivity(this); - remoting.app.setConnectionMode(remoting.Application.Mode.ME2ME); - this.desktopActivity_.start(this.host_, this.createCredentialsProvider_(), - suppressHostOfflineError); + var connector = remoting.SessionConnector.factory.createConnector( + document.getElementById('client-container'), + remoting.app_capabilities(), + this.desktopActivity_); + + connector.connect( + remoting.Application.Mode.ME2ME, + this.host_, this.createCredentialsProvider_(), suppressHostOfflineError); }; /** diff --git a/remoting/webapp/crd/js/mock_client_plugin.js b/remoting/webapp/crd/js/mock_client_plugin.js index 749a68b..965e213 100644 --- a/remoting/webapp/crd/js/mock_client_plugin.js +++ b/remoting/webapp/crd/js/mock_client_plugin.js @@ -5,6 +5,7 @@ /** * @fileoverview * Mock implementation of ClientPlugin for testing. + * @suppress {checkTypes} */ 'use strict'; @@ -19,16 +20,12 @@ var remoting = remoting || {}; */ remoting.MockClientPlugin = function(container) { this.container_ = container; - this.element_ = /** @type {HTMLElement} */ (document.createElement('div')); + this.element_ = document.createElement('div'); this.element_.style.backgroundImage = 'linear-gradient(45deg, blue, red)'; + this.connectionStatusUpdateHandler_ = null; + this.desktopSizeUpdateHandler_ = null; this.container_.appendChild(this.element_); this.hostDesktop_ = new remoting.MockClientPlugin.HostDesktop(); - this.extensions_ = new remoting.ProtocolExtensionManager(base.doNothing); - /** @type {remoting.ClientPlugin.ConnectionEventHandler} */ - this.connectionEventHandler = null; - - // Fake initialization result to return. - this.mock$initializationResult = true; }; remoting.MockClientPlugin.prototype.dispose = function() { @@ -37,10 +34,6 @@ remoting.MockClientPlugin.prototype.dispose = function() { this.connectionStatusUpdateHandler_ = null; }; -remoting.MockClientPlugin.prototype.extensions = function() { - return this.extensions_; -}; - remoting.MockClientPlugin.prototype.hostDesktop = function() { return this.hostDesktop_; }; @@ -50,31 +43,24 @@ remoting.MockClientPlugin.prototype.element = function() { }; remoting.MockClientPlugin.prototype.initialize = function(onDone) { - var that = this; - Promise.resolve().then(function() { - onDone(that.mock$initializationResult); - }); + window.setTimeout(onDone.bind(null, true), 0); }; remoting.MockClientPlugin.prototype.connect = function(host, localJid, credentialsProvider) { - base.debug.assert(this.connectionEventHandler !== null); - var that = this; - window.requestAnimationFrame(function() { - that.connectionEventHandler.onConnectionStatusUpdate( + base.debug.assert(this.connectionStatusUpdateHandler_ != null); + window.setTimeout( + this.connectionStatusUpdateHandler_.bind( + this, remoting.ClientSession.State.CONNECTED, - remoting.ClientSession.ConnectionError.NONE); - }); + remoting.ClientSession.ConnectionError.NONE), + 0); }; -remoting.MockClientPlugin.prototype.injectKeyCombination = function(keys) {}; - remoting.MockClientPlugin.prototype.injectKeyEvent = function(key, down) {}; -remoting.MockClientPlugin.prototype.setRemapKeys = function(remappings) {}; - remoting.MockClientPlugin.prototype.remapKey = function(from, to) {}; remoting.MockClientPlugin.prototype.releaseAllKeys = function() {}; @@ -95,8 +81,6 @@ remoting.MockClientPlugin.prototype.sendClipboardItem = remoting.MockClientPlugin.prototype.requestPairing = function(clientName, onDone) {}; -remoting.MockClientPlugin.prototype.allowMouseLock = function() {}; - remoting.MockClientPlugin.prototype.pauseAudio = function(pause) {}; remoting.MockClientPlugin.prototype.pauseVideo = function(pause) {}; @@ -113,17 +97,41 @@ remoting.MockClientPlugin.prototype.getPerfStats = function() { return result; }; -remoting.MockClientPlugin.prototype.setConnectionEventHandler = +remoting.MockClientPlugin.prototype.sendClientMessage = + function(name, data) {}; + +remoting.MockClientPlugin.prototype.setOnOutgoingIqHandler = + function(handler) {}; + +remoting.MockClientPlugin.prototype.setOnDebugMessageHandler = + function(handler) {}; + +/** + * @param {function(number, number):void} handler + * @private + */ +remoting.MockClientPlugin.prototype.setConnectionStatusUpdateHandler = function(handler) { - this.connetionEventHandler = handler; + /** @type {function(number, number):void} */ + this.connectionStatusUpdateHandler_ = handler; }; -remoting.MockClientPlugin.prototype.setMouseCursorHandler = +remoting.MockClientPlugin.prototype.setRouteChangedHandler = + function(handler) {}; + +remoting.MockClientPlugin.prototype.setConnectionReadyHandler = + function(handler) {}; + +remoting.MockClientPlugin.prototype.setCapabilitiesHandler = + function(handler) {}; + +remoting.MockClientPlugin.prototype.setGnubbyAuthHandler = function(handler) {}; -remoting.MockClientPlugin.prototype.setClipboardHandler = function(handler) {}; +remoting.MockClientPlugin.prototype.setCastExtensionHandler = + function(handler) {}; -remoting.MockClientPlugin.prototype.setDebugDirtyRegionHandler = +remoting.MockClientPlugin.prototype.setMouseCursorHandler = function(handler) {}; /** @@ -184,17 +192,13 @@ remoting.MockClientPlugin.HostDesktop.prototype.resize = /** * @constructor - * @implements {remoting.ClientPluginFactory} + * @extends {remoting.ClientPluginFactory} */ -remoting.MockClientPluginFactory = function() { - /** @private {remoting.MockClientPlugin} */ - this.plugin_ = null; -}; +remoting.MockClientPluginFactory = function() {}; remoting.MockClientPluginFactory.prototype.createPlugin = function(container, onExtensionMessage) { - this.plugin_ = new remoting.MockClientPlugin(container); - return this.plugin_; + return new remoting.MockClientPlugin(container); }; remoting.MockClientPluginFactory.prototype.preloadPlugin = function() {}; diff --git a/remoting/webapp/crd/js/mock_session_connector.js b/remoting/webapp/crd/js/mock_session_connector.js new file mode 100644 index 0000000..ae7d184 --- /dev/null +++ b/remoting/webapp/crd/js/mock_session_connector.js @@ -0,0 +1,171 @@ +// 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 + * Mock implementation of SessionConnector for testing. + * @suppress {checkTypes} + */ + +'use strict'; + +/** @suppress {duplicate} */ +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. + * @param {function(!remoting.Error):void} onConnectionFailed Callback for when + * the connection fails. + * @param {Array<string>} requiredCapabilities Connector capabilities + * required by this application. + * @param {string} defaultRemapKeys The default set of key mappings for the + * client session to use. + * @constructor + * @implements {remoting.SessionConnector} + */ +remoting.MockSessionConnector = function(clientContainer, onConnected, onError, + onExtensionMessage, + onConnectionFailed, + requiredCapabilities, + defaultRemapKeys) { + this.clientContainer_ = clientContainer; + /** @type {function(remoting.ClientSession):void} */ + this.onConnected_ = onConnected; + this.onError = onError; + this.onExtensionMessage_ = onExtensionMessage; + this.onConnectionFailed_ = onConnectionFailed; + this.requiredCapabilities_ = requiredCapabilities; + this.defaultRemapKeys_ = defaultRemapKeys; + + this.reset(); +}; + +remoting.MockSessionConnector.prototype.reset = function() { + /** @type {string} @private */ + this.hostId_ = ''; +}; + +remoting.MockSessionConnector.prototype.connectMe2Me = + function(host, fetchPin, fetchThirdPartyToken, + clientPairingId, clientPairedSecret) { + this.connect_(); +}; + +remoting.MockSessionConnector.prototype.retryConnectMe2Me = + function(host, fetchPin, fetchThirdPartyToken, + clientPairingId, clientPairedSecret) { + this.connect_(); +}; + +remoting.MockSessionConnector.prototype.connectMe2App = + function(host, fetchThirdPartyToken) { + this.connect_(); +}; + +remoting.MockSessionConnector.prototype.updatePairingInfo = + function(clientId, sharedSecret) { +}; + +remoting.MockSessionConnector.prototype.connectIT2Me = + function(accessCode) { + this.connect_(); +}; + +remoting.MockSessionConnector.prototype.reconnect = function() { + this.connect_(); +}; + +remoting.MockSessionConnector.prototype.cancel = function() { +}; + +remoting.MockSessionConnector.prototype.getHostId = function() { + return this.hostId_; +}; + +remoting.MockSessionConnector.prototype.connect_ = function() { + var signalling = new remoting.MockSignalStrategy(); + signalling.setStateForTesting(remoting.SignalStrategy.State.CONNECTED); + var hostName = 'Mock host'; + var accessCode = ''; + var authenticationMethods = ''; + var hostId = ''; + var hostJid = ''; + var hostPublicKey = ''; + var clientPairingId = ''; + var clientPairedSecret = ''; + + /** + * @param {boolean} offerPairing + * @param {function(string):void} callback + */ + var fetchPin = function(offerPairing, callback) { + window.setTimeout(function() { callback('') }, 0); + }; + + /** + * @param {string} tokenUrl + * @param {string} hostPublicKey + * @param {string} scope + * @param {function(string, string):void} callback + */ + var fetchThirdPartyToken = function(tokenUrl, hostPublicKey, scope, + callback) { + window.setTimeout(function() { callback('', '') }, 0); + }; + + var clientSession = new remoting.ClientSession( + signalling, this.clientContainer_, hostName, + accessCode, fetchPin, fetchThirdPartyToken, + authenticationMethods, hostId, hostJid, hostPublicKey, + clientPairingId, clientPairedSecret); + + var that = this; + /** @param {remoting.ClientSession.StateEvent} event */ + var onStateChange = function(event) { + if (event.current == remoting.ClientSession.State.CONNECTED) { + that.onConnected_(clientSession); + } + }; + + clientSession.addEventListener( + remoting.ClientSession.Events.stateChanged, + onStateChange); +}; + + +/** + * @constructor + * @extends {remoting.SessionConnectorFactory} + */ +remoting.MockSessionConnectorFactory = function() {}; + +/** + * @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. + * @param {function(!remoting.Error):void} onConnectionFailed Callback for when + * the connection fails. + * @param {Array<string>} requiredCapabilities Connector capabilities + * required by this application. + * @param {string} defaultRemapKeys The default set of key mappings to use + * in the client session. + * @return {remoting.MockSessionConnector} + */ +remoting.MockSessionConnectorFactory.prototype.createConnector = + function(clientContainer, onConnected, onError, onExtensionMessage, + onConnectionFailed, requiredCapabilities, defaultRemapKeys) { + return new remoting.MockSessionConnector( + clientContainer, onConnected, onError, onExtensionMessage, + onConnectionFailed, requiredCapabilities, defaultRemapKeys); +}; diff --git a/remoting/webapp/crd/js/session_connector.js b/remoting/webapp/crd/js/session_connector.js new file mode 100644 index 0000000..4481f5c --- /dev/null +++ b/remoting/webapp/crd/js/session_connector.js @@ -0,0 +1,52 @@ +// 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 + * Interface abstracting the SessionConnector functionality. + */ + +'use strict'; + +/** @suppress {duplicate} */ +var remoting = remoting || {}; + +/** + * @interface + */ +remoting.SessionConnector = function() {}; + +/** + * Initiates a remote connection. + * + * @param {remoting.Application.Mode} mode + * @param {remoting.Host} host + * @param {remoting.CredentialsProvider} credentialsProvider + * @param {boolean=} opt_suppressOfflineError Suppress the host offline error + * as we use stale JID's when initiating a Me2Me. This parameter will be + * removed when we get rid of sessionConnector altogether in a future CL. + * @return {void} Nothing. + */ +remoting.SessionConnector.prototype.connect = + function(mode, host, credentialsProvider, opt_suppressOfflineError) {}; + +/** + * @interface + */ +remoting.SessionConnectorFactory = function() {}; + +/** + * @param {HTMLElement} clientContainer Container element for the client view. + * @param {Array<string>} requiredCapabilities Connector capabilities + * required by this application. + * @param {remoting.ClientSession.EventHandler} handler + * @return {remoting.SessionConnector} + */ +remoting.SessionConnectorFactory.prototype.createConnector = + function(clientContainer, requiredCapabilities, handler) {}; + +/** + * @type {remoting.SessionConnectorFactory} + */ +remoting.SessionConnector.factory = null; diff --git a/remoting/webapp/crd/js/session_connector_impl.js b/remoting/webapp/crd/js/session_connector_impl.js new file mode 100644 index 0000000..438a1f0 --- /dev/null +++ b/remoting/webapp/crd/js/session_connector_impl.js @@ -0,0 +1,291 @@ +// Copyright 2013 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 + */ + +'use strict'; + +/** @suppress {duplicate} */ +var remoting = remoting || {}; + +/** + * @param {HTMLElement} clientContainer Container element for the client view. + * @param {Array<string>} requiredCapabilities Connector capabilities + * required by this application. + * @param {remoting.ClientSession.EventHandler} handler + * @constructor + * @implements {remoting.SessionConnector} + */ +remoting.SessionConnectorImpl = + function(clientContainer, requiredCapabilities, handler) { + /** @private {HTMLElement} */ + this.clientContainer_ = clientContainer; + + /** @private */ + this.onError_ = handler.onError.bind(handler); + + /** @private */ + this.handler_ = handler; + + /** @private {Array<string>} */ + this.requiredCapabilities_ = [ + remoting.ClientSession.Capability.SEND_INITIAL_RESOLUTION, + remoting.ClientSession.Capability.RATE_LIMIT_RESIZE_REQUESTS, + remoting.ClientSession.Capability.VIDEO_RECORDER + ]; + + // Append the app-specific capabilities. + this.requiredCapabilities_.push.apply(this.requiredCapabilities_, + requiredCapabilities); + + // Initialize/declare per-connection state. + this.closeSession_(); +}; + +/** + * 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. + * @private + */ +remoting.SessionConnectorImpl.prototype.closeSession_ = function() { + // It's OK to initialize these member variables here because the + // constructor calls this method. + + base.dispose(this.eventHook_); + /** @private {base.Disposable} */ + this.eventHook_ = null; + + /** @private {remoting.Host} */ + this.host_ = null; + + /** @private {boolean} */ + this.logHostOfflineErrors_ = false; + + base.dispose(this.clientSession_); + /** @private {remoting.ClientSession} */ + this.clientSession_ = null; + + base.dispose(this.plugin_); + /** @private {remoting.ClientPlugin} */ + this.plugin_ = null; + + /** @private {remoting.CredentialsProvider} */ + this.credentialsProvider_ = null; + + base.dispose(this.signalStrategy_); + /** @private {remoting.SignalStrategy} */ + this.signalStrategy_ = null; +}; + +/** + * Initiates a connection. + * + * @param {remoting.Application.Mode} mode + * @param {remoting.Host} host the Host to connect to. + * @param {remoting.CredentialsProvider} credentialsProvider + * @param {boolean=} opt_suppressOfflineError + * @return {void} Nothing. + * @private + */ +remoting.SessionConnectorImpl.prototype.connect = + function(mode, host, credentialsProvider, opt_suppressOfflineError) { + // In some circumstances, the WCS <iframe> can get reloaded, which results + // in a new clientJid and a new callback. In this case, cancel any existing + // connect operation and remove the old client plugin before instantiating a + // new one. + this.closeSession_(); + remoting.app.setConnectionMode(mode); + this.host_ = host; + this.credentialsProvider_ = credentialsProvider; + this.logHostOfflineErrors_ = !Boolean(opt_suppressOfflineError); + this.connectSignaling_(); +}; + +/** + * @private + */ +remoting.SessionConnectorImpl.prototype.connectSignaling_ = function() { + base.dispose(this.signalStrategy_); + this.signalStrategy_ = null; + + /** @type {remoting.SessionConnectorImpl} */ + var that = this; + + /** @param {string} token */ + function connectSignalingWithToken(token) { + remoting.identity.getUserInfo().then( + connectSignalingWithTokenAndUserInfo.bind(null, token), + remoting.Error.handler(that.onError_)); + } + + /** + * Success callback for when the email and fullName have been retrieved + * for this user. + * Note that the full name will be null unless the webapp has requested + * and been granted the userinfo.profile permission. + * + * @param {string} token + * @param {{email: string, name: string}} userInfo + */ + function connectSignalingWithTokenAndUserInfo(token, userInfo) { + that.signalStrategy_.connect(remoting.settings.XMPP_SERVER, userInfo.email, + token); + } + + this.signalStrategy_ = remoting.SignalStrategy.create(); + this.signalStrategy_.setStateChangedCallback( + this.onSignalingState_.bind(this)); + + remoting.identity.getToken().then( + connectSignalingWithToken, + remoting.Error.handler(this.onError_)); +}; + +/** + * @private + * @param {remoting.SignalStrategy.State} state + */ +remoting.SessionConnectorImpl.prototype.onSignalingState_ = function(state) { + switch (state) { + case remoting.SignalStrategy.State.CONNECTED: + // Proceed only if the connection hasn't been canceled. + if (this.host_.jabberId) { + this.createSession_(); + } + break; + + case remoting.SignalStrategy.State.FAILED: + this.onError_(this.signalStrategy_.getError()); + break; + } +}; + +/** + * Creates ClientSession object. + */ +remoting.SessionConnectorImpl.prototype.createSession_ = function() { + var pluginContainer = this.clientContainer_.querySelector( + '.client-plugin-container'); + + this.plugin_ = remoting.ClientPlugin.factory.createPlugin( + pluginContainer, this.requiredCapabilities_); + + var that = this; + this.host_.options.load().then(function(){ + that.plugin_.initialize(that.onPluginInitialized_.bind(that)); + }); +}; + +/** + * @param {boolean} initialized + * @private + */ +remoting.SessionConnectorImpl.prototype.onPluginInitialized_ = function( + initialized) { + if (!initialized) { + console.error('ERROR: remoting plugin not loaded'); + this.pluginError_(new remoting.Error(remoting.Error.Tag.MISSING_PLUGIN)); + return; + } + + if (!this.plugin_.isSupportedVersion()) { + console.error('ERROR: bad plugin version'); + this.pluginError_(new remoting.Error( + remoting.Error.Tag.BAD_PLUGIN_VERSION)); + return; + } + + this.clientSession_ = new remoting.ClientSession( + this.plugin_, this.host_, this.signalStrategy_); + + this.clientSession_.logHostOfflineErrors(this.logHostOfflineErrors_); + this.eventHook_ = new base.EventHook( + this.clientSession_, 'stateChanged', this.onStateChange_.bind(this)); + this.plugin_.connect( + this.host_, this.signalStrategy_.getJid(), this.credentialsProvider_); +}; + +/** + * @param {!remoting.Error} error + * @private + */ +remoting.SessionConnectorImpl.prototype.pluginError_ = function(error) { + this.signalStrategy_.setIncomingStanzaCallback(null); + this.closeSession_(); +}; + +/** + * Handle a change in the state of the client session. + * + * @param {remoting.ClientSession.StateEvent=} event + * @return {void} Nothing. + * @private + */ +remoting.SessionConnectorImpl.prototype.onStateChange_ = function(event) { + switch (event.current) { + case remoting.ClientSession.State.CONNECTED: + var connectionInfo = new remoting.ConnectionInfo( + this.host_, this.credentialsProvider_, this.clientSession_, + this.plugin_); + this.handler_.onConnected(connectionInfo); + break; + + case remoting.ClientSession.State.CONNECTING: + remoting.identity.getEmail().then(function(/** string */ email) { + console.log('Connecting as ' + email); + }); + break; + + case remoting.ClientSession.State.AUTHENTICATED: + console.log('Connection authenticated'); + break; + + case remoting.ClientSession.State.INITIALIZING: + console.log('Initializing connection'); + break; + + case remoting.ClientSession.State.CLOSED: + this.handler_.onDisconnected(); + break; + + case remoting.ClientSession.State.FAILED: + var error = this.clientSession_.getError(); + console.error('Client plugin reported connection failed: ' + + error.toString()); + this.handler_.onConnectionFailed(error || remoting.Error.unexpected()); + 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_(new remoting.Error(remoting.Error.Tag.MISSING_PLUGIN)); + } + + if (this.clientSession_.isFinished()) { + this.closeSession_(); + } +}; + +/** + * @constructor + * @implements {remoting.SessionConnectorFactory} + */ +remoting.DefaultSessionConnectorFactory = function() {}; + +/** + * @param {HTMLElement} clientContainer Container element for the client view. + * @param {Array<string>} requiredCapabilities Connector capabilities + * required by this application. + * @param {remoting.ClientSession.EventHandler} handler + * @return {remoting.SessionConnector} + */ +remoting.DefaultSessionConnectorFactory.prototype.createConnector = + function(clientContainer, requiredCapabilities, handler) { + return new remoting.SessionConnectorImpl(clientContainer, + requiredCapabilities, handler); +}; |