Chromium Code Reviews| OLD | NEW |
|---|---|
| 1 // Copyright 2014 The Chromium Authors. All rights reserved. | 1 // Copyright 2014 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 * Interface abstracting the Application functionality. | 7 * Interface abstracting the Application functionality. |
| 8 */ | 8 */ |
| 9 | 9 |
| 10 'use strict'; | 10 'use strict'; |
| 11 | 11 |
| 12 /** @suppress {duplicate} */ | 12 /** @suppress {duplicate} */ |
| 13 var remoting = remoting || {}; | 13 var remoting = remoting || {}; |
| 14 | 14 |
| 15 /** | 15 /** |
| 16 * The interface specifies the methods that a subclass of remoting.Application | |
| 17 * is required implement to override the default behavior. | |
| 18 * | |
| 19 * @interface | |
| 20 */ | |
| 21 remoting.ApplicationInterface = function() {}; | |
| 22 | |
| 23 /** | |
| 24 * @return {string} Application product name to be used in UI. | |
| 25 */ | |
| 26 remoting.ApplicationInterface.prototype.getApplicationName = function() {}; | |
| 27 | |
| 28 /** | |
| 29 * Report an authentication error to the user. This is called in lieu of | |
| 30 * startApplication() if the user cannot be authenticated or if they decline | |
| 31 * the app permissions. | |
| 32 * | |
| 33 * @param {!remoting.Error} error The failure reason. | |
| 34 */ | |
| 35 remoting.ApplicationInterface.prototype.signInFailed = function(error) {}; | |
| 36 | |
| 37 /** | |
| 38 * Initialize the application. This is called before an OAuth token is requested | |
| 39 * and should be used for tasks such as initializing the DOM, registering event | |
| 40 * handlers, etc. After this is called, the app is running and waiting for | |
| 41 * user events. | |
| 42 * | |
| 43 * @return {void} Nothing. | |
| 44 */ | |
| 45 remoting.ApplicationInterface.prototype.initApplication = function() {}; | |
| 46 | |
| 47 /** | |
| 48 * Start the application. Once startApplication() is called, we can assume that | |
| 49 * the user has consented to all permissions specified in the manifest. | |
| 50 * | |
| 51 * @param {string} token An OAuth access token. The app should not cache | |
| 52 * this token, but can assume that it will remain valid during application | |
| 53 * start-up. | |
| 54 */ | |
| 55 remoting.ApplicationInterface.prototype.startApplication = function(token) {}; | |
| 56 | |
| 57 | |
| 58 /** | |
| 16 * @param {Array<string>} appCapabilities Array of application capabilities. | 59 * @param {Array<string>} appCapabilities Array of application capabilities. |
| 17 * @constructor | 60 * @constructor |
| 61 * @implements {remoting.ApplicationInterface} | |
| 18 */ | 62 */ |
| 19 remoting.Application = function(appCapabilities) { | 63 remoting.Application = function(appCapabilities) { |
| 20 /** @private {remoting.Application.Delegate} */ | 64 // Create global objects. |
| 21 this.delegate_ = null; | 65 remoting.ClientPlugin.factory = new remoting.DefaultClientPluginFactory(); |
| 66 remoting.SessionConnector.factory = | |
| 67 new remoting.DefaultSessionConnectorFactory(); | |
|
garykac
2015/03/23 18:44:22
Moved here from start() since this is required to
Jamie
2015/03/25 20:00:39
The purpose of these factories is to allow test co
garykac
2015/03/26 01:41:57
Previously, they were setup in the start() functio
| |
| 22 | 68 |
| 23 /** @private {Array<string>} */ | 69 /** @private {Array<string>} */ |
| 24 this.appCapabilities_ = [ | 70 this.appCapabilities_ = [ |
| 25 remoting.ClientSession.Capability.SEND_INITIAL_RESOLUTION, | 71 remoting.ClientSession.Capability.SEND_INITIAL_RESOLUTION, |
| 26 remoting.ClientSession.Capability.RATE_LIMIT_RESIZE_REQUESTS, | 72 remoting.ClientSession.Capability.RATE_LIMIT_RESIZE_REQUESTS, |
| 27 remoting.ClientSession.Capability.VIDEO_RECORDER | 73 remoting.ClientSession.Capability.VIDEO_RECORDER |
| 28 ]; | 74 ]; |
| 29 // Append the app-specific capabilities. | 75 // Append the app-specific capabilities. |
| 30 this.appCapabilities_.push.apply(this.appCapabilities_, appCapabilities); | 76 this.appCapabilities_.push.apply(this.appCapabilities_, appCapabilities); |
| 31 | 77 |
| 32 /** @private {remoting.SessionConnector} */ | 78 /** @protected {remoting.SessionConnector} */ |
| 33 this.sessionConnector_ = null; | 79 this.sessionConnector_ = remoting.SessionConnector.factory.createConnector( |
|
garykac
2015/03/23 18:44:22
Moved here from getter.
| |
| 80 document.getElementById('client-container'), | |
| 81 this.onConnected.bind(this), | |
| 82 this.onError.bind(this), | |
| 83 this.onConnectionFailed.bind(this), | |
| 84 this.appCapabilities_); | |
| 34 | 85 |
| 35 /** @private {base.Disposable} */ | 86 /** @private {base.Disposable} */ |
| 36 this.sessionConnectedHooks_ = null; | 87 this.sessionConnectedHooks_ = null; |
| 37 }; | 88 }; |
| 38 | 89 |
| 39 /** | 90 /** |
| 40 * @param {remoting.Application.Delegate} appDelegate The delegate that | 91 * @return {remoting.SessionConnector} The session connector. |
| 41 * contains the app-specific functionality. | |
| 42 */ | 92 */ |
| 43 remoting.Application.prototype.setDelegate = function(appDelegate) { | 93 remoting.Application.prototype.getSessionConnector = function() { |
| 44 this.delegate_ = appDelegate; | 94 return this.sessionConnector_; |
| 45 }; | 95 }; |
| 46 | 96 |
| 47 /** | 97 /** |
| 48 * @return {string} Application product name to be used in UI. | |
| 49 */ | |
| 50 remoting.Application.prototype.getApplicationName = function() { | |
| 51 return this.delegate_.getApplicationName(); | |
| 52 }; | |
| 53 | |
| 54 /** | |
| 55 * @param {remoting.ClientSession.Capability} capability | 98 * @param {remoting.ClientSession.Capability} capability |
| 56 * @return {boolean} | 99 * @return {boolean} |
| 57 */ | 100 */ |
| 58 remoting.Application.prototype.hasCapability = function(capability) { | 101 remoting.Application.prototype.hasCapability = function(capability) { |
| 59 var capabilities = this.appCapabilities_; | 102 var capabilities = this.appCapabilities_; |
| 60 return capabilities.indexOf(capability) != -1; | 103 return capabilities.indexOf(capability) != -1; |
| 61 }; | 104 }; |
| 62 | 105 |
| 106 | |
| 107 /** @return {string} */ | |
|
Jamie
2015/03/24 21:57:05
AFAICT, these functions are essentially boilerplat
garykac
2015/03/26 01:41:57
Done.
| |
| 108 remoting.Application.prototype.getApplicationName = function() { | |
| 109 throw "Subclass must override"; | |
|
Jamie
2015/03/24 21:57:05
base.assert would be more appropriate here.
garykac
2015/03/26 01:41:57
Done.
| |
| 110 }; | |
| 111 | |
| 112 /** @param {!remoting.Error} error */ | |
| 113 remoting.Application.prototype.signInFailed = function(error) { | |
| 114 throw "Subclass must override"; | |
| 115 }; | |
| 116 | |
| 117 remoting.Application.prototype.initApplication = function() { | |
| 118 throw "Subclass must override"; | |
| 119 }; | |
| 120 | |
| 121 /** @param {string} token */ | |
| 122 remoting.Application.prototype.startApplication = function(token) { | |
| 123 throw "Subclass must override"; | |
| 124 }; | |
| 125 | |
| 126 | |
| 63 /** | 127 /** |
| 64 * Initialize the application and register all event handlers. After this | 128 * Initialize the application and register all event handlers. After this |
| 65 * is called, the app is running and waiting for user events. | 129 * is called, the app is running and waiting for user events. |
| 66 * | 130 * |
| 67 * @return {void} Nothing. | 131 * @return {void} Nothing. |
| 68 */ | 132 */ |
| 69 remoting.Application.prototype.start = function() { | 133 remoting.Application.prototype.start = function() { |
| 70 // Create global objects. | |
| 71 remoting.ClientPlugin.factory = new remoting.DefaultClientPluginFactory(); | |
| 72 remoting.SessionConnector.factory = | |
| 73 new remoting.DefaultSessionConnectorFactory(); | |
| 74 | |
| 75 // TODO(garykac): This should be owned properly rather than living in the | 134 // TODO(garykac): This should be owned properly rather than living in the |
| 76 // global 'remoting' namespace. | 135 // global 'remoting' namespace. |
| 77 remoting.settings = new remoting.Settings(); | 136 remoting.settings = new remoting.Settings(); |
| 78 | 137 |
| 79 remoting.initGlobalObjects(); | 138 remoting.initGlobalObjects(); |
| 80 remoting.initIdentity(); | 139 remoting.initIdentity(); |
| 81 | 140 |
| 82 this.delegate_.init(); | 141 this.initApplication(); |
| 83 | 142 |
| 84 var that = this; | 143 var that = this; |
| 85 remoting.identity.getToken().then( | 144 remoting.identity.getToken(). |
| 86 this.delegate_.start.bind(this.delegate_, this.getSessionConnector()) | 145 then(this.startApplication.bind(this)). |
| 87 ).catch(remoting.Error.handler( | 146 catch(remoting.Error.handler( |
| 88 function(/** !remoting.Error */ error) { | 147 function(/** !remoting.Error */ error) { |
| 89 if (error.hasTag(remoting.Error.Tag.CANCELLED)) { | 148 if (error.hasTag(remoting.Error.Tag.CANCELLED)) { |
| 90 that.exit(); | 149 that.exit(); |
| 91 } else { | 150 } else { |
| 92 that.delegate_.signInFailed(error); | 151 that.signInFailed(error); |
| 93 } | 152 } |
| 94 } | 153 } |
| 95 ) | 154 ) |
| 96 ); | 155 ); |
| 97 }; | 156 }; |
| 98 | 157 |
| 99 /** | 158 /** |
| 100 * Quit the application. | 159 * Quit the application. |
| 101 */ | 160 */ |
| 102 remoting.Application.prototype.exit = function() { | 161 remoting.Application.prototype.exit = function() { |
| 103 this.delegate_.handleExit(); | |
|
Jamie
2015/03/24 21:57:05
I'm concerned about a loss of readability here. Wi
Jamie
2015/03/25 20:00:39
As discussed off-line, please refactor so that all
garykac
2015/03/26 01:41:57
Done.
| |
| 104 chrome.app.window.current().close(); | 162 chrome.app.window.current().close(); |
| 105 }; | 163 }; |
| 106 | 164 |
| 107 /** Disconnect the remoting client. */ | 165 /** Disconnect the remoting client. */ |
| 108 remoting.Application.prototype.disconnect = function() { | 166 remoting.Application.prototype.disconnect = function() { |
| 109 if (remoting.clientSession) { | 167 if (remoting.clientSession) { |
| 110 remoting.clientSession.disconnect(remoting.Error.none()); | 168 remoting.clientSession.disconnect(remoting.Error.none()); |
| 111 console.log('Disconnected.'); | 169 console.log('Disconnected.'); |
| 112 } | 170 } |
| 113 }; | 171 }; |
| 114 | 172 |
| 115 /** | |
| 116 * Called when a new session has been connected. | |
| 117 * | |
| 118 * @param {remoting.ConnectionInfo} connectionInfo | |
| 119 * @return {void} Nothing. | |
| 120 */ | |
| 121 remoting.Application.prototype.onConnected = function(connectionInfo) { | |
|
garykac
2015/03/23 18:44:22
This has been moved a bit further down so that it
| |
| 122 this.sessionConnectedHooks_ = new base.Disposables( | |
| 123 new base.EventHook(connectionInfo.session(), 'stateChanged', | |
| 124 this.onSessionFinished_.bind(this)), | |
| 125 new base.RepeatingTimer(this.updateStatistics_.bind(this), 1000) | |
| 126 ); | |
| 127 remoting.clipboard.startSession(); | |
| 128 | |
| 129 this.delegate_.handleConnected(connectionInfo); | |
| 130 }; | |
| 131 | 173 |
| 132 /** | 174 /** |
| 133 * Called when the current session has been disconnected. | 175 * Called when the current session has been disconnected. |
| 134 * | 176 * |
| 135 * @return {void} Nothing. | 177 * @return {void} Nothing. |
| 136 */ | 178 */ |
| 137 remoting.Application.prototype.onDisconnected = function() { | 179 remoting.Application.prototype.onDisconnected = function() { |
|
Jamie
2015/03/25 20:00:39
Under the new "pure-virtual or final" model, I thi
garykac
2015/03/26 01:41:57
All of them except getApplicationName are now @pro
| |
| 138 this.delegate_.handleDisconnected(); | |
|
garykac
2015/03/23 18:44:22
These redirects are no longer needed. Subclass wil
| |
| 139 }; | 180 }; |
| 140 | 181 |
| 141 /** | 182 /** |
| 142 * Called when the current session's connection has failed. | 183 * Called when the current session's connection has failed. |
| 143 * | 184 * |
| 144 * @param {!remoting.Error} error | 185 * @param {!remoting.Error} error |
| 145 * @return {void} Nothing. | 186 * @return {void} Nothing. |
| 146 */ | 187 */ |
| 147 remoting.Application.prototype.onConnectionFailed = function(error) { | 188 remoting.Application.prototype.onConnectionFailed = function(error) { |
| 148 this.delegate_.handleConnectionFailed(this.sessionConnector_, error); | |
| 149 }; | 189 }; |
| 150 | 190 |
| 151 /** | 191 /** |
| 152 * Called when an error needs to be displayed to the user. | 192 * Called when an error needs to be displayed to the user. |
| 153 * | 193 * |
| 154 * @param {!remoting.Error} errorTag The error to be localized and displayed. | 194 * @param {!remoting.Error} errorTag The error to be localized and displayed. |
| 155 * @return {void} Nothing. | 195 * @return {void} Nothing. |
| 156 */ | 196 */ |
| 157 remoting.Application.prototype.onError = function(errorTag) { | 197 remoting.Application.prototype.onError = function(errorTag) { |
| 158 this.delegate_.handleError(errorTag); | |
| 159 }; | 198 }; |
| 160 | 199 |
| 161 /** | 200 /** |
| 162 * @return {remoting.SessionConnector} A session connector, creating a new one | 201 * Called when a new session has been connected. |
| 163 * if necessary. | 202 * |
| 203 * @param {remoting.ConnectionInfo} connectionInfo | |
| 204 * @return {void} Nothing. | |
| 164 */ | 205 */ |
| 165 remoting.Application.prototype.getSessionConnector = function() { | 206 remoting.Application.prototype.onConnected = function(connectionInfo) { |
| 166 // TODO(garykac): Check if this can be initialized in the ctor. | 207 this.sessionConnectedHooks_ = new base.Disposables( |
|
garykac
2015/03/23 18:44:22
Recent changes make this easy to move into the con
| |
| 167 if (!this.sessionConnector_) { | 208 new base.EventHook(connectionInfo.session(), 'stateChanged', |
| 168 this.sessionConnector_ = remoting.SessionConnector.factory.createConnector( | 209 this.onSessionFinished_.bind(this)), |
| 169 document.getElementById('client-container'), | 210 new base.RepeatingTimer(this.updateStatistics_.bind(this), 1000) |
| 170 this.onConnected.bind(this), | 211 ); |
| 171 this.onError.bind(this), | 212 remoting.clipboard.startSession(); |
| 172 this.onConnectionFailed.bind(this), | |
| 173 this.appCapabilities_); | |
| 174 } | |
| 175 return this.sessionConnector_; | |
| 176 }; | 213 }; |
| 177 | 214 |
| 178 /** | 215 /** |
| 179 * Callback function called when the state of the client plugin changes. The | 216 * Callback function called when the state of the client plugin changes. The |
| 180 * current and previous states are available via the |state| member variable. | 217 * current and previous states are available via the |state| member variable. |
| 181 * | 218 * |
| 182 * @param {remoting.ClientSession.StateEvent=} state | 219 * @param {remoting.ClientSession.StateEvent=} state |
| 183 * @private | 220 * @private |
| 184 */ | 221 */ |
| 185 remoting.Application.prototype.onSessionFinished_ = function(state) { | 222 remoting.Application.prototype.onSessionFinished_ = function(state) { |
| (...skipping 26 matching lines...) Expand all Loading... | |
| 212 }; | 249 }; |
| 213 | 250 |
| 214 /** @private */ | 251 /** @private */ |
| 215 remoting.Application.prototype.updateStatistics_ = function() { | 252 remoting.Application.prototype.updateStatistics_ = function() { |
| 216 var perfstats = remoting.clientSession.getPerfStats(); | 253 var perfstats = remoting.clientSession.getPerfStats(); |
| 217 remoting.stats.update(perfstats); | 254 remoting.stats.update(perfstats); |
| 218 remoting.clientSession.logStatistics(perfstats); | 255 remoting.clientSession.logStatistics(perfstats); |
| 219 }; | 256 }; |
| 220 | 257 |
| 221 | 258 |
| 222 /** | |
| 223 * @interface | |
| 224 */ | |
| 225 remoting.Application.Delegate = function() {}; | |
| 226 | |
| 227 /** | |
| 228 * Initialize the application. This is called before an OAuth token is requested | |
| 229 * and should be used for tasks such as initializing the DOM, registering event | |
| 230 * handlers, etc. | |
| 231 */ | |
| 232 remoting.Application.Delegate.prototype.init = function() {}; | |
| 233 | |
| 234 /** | |
| 235 * Start the application. Once start() is called, the delegate can assume that | |
| 236 * the user has consented to all permissions specified in the manifest. | |
| 237 * | |
| 238 * @param {remoting.SessionConnector} connector | |
| 239 * @param {string} token An OAuth access token. The delegate should not cache | |
| 240 * this token, but can assume that it will remain valid during application | |
| 241 * start-up. | |
| 242 */ | |
| 243 remoting.Application.Delegate.prototype.start = function(connector, token) {}; | |
| 244 | |
| 245 /** | |
| 246 * Report an authentication error to the user. This is called in lieu of start() | |
| 247 * if the user cannot be authenticated. | |
| 248 * | |
| 249 * @param {!remoting.Error} error The failure reason. | |
| 250 */ | |
| 251 remoting.Application.Delegate.prototype.signInFailed = function(error) {}; | |
| 252 | |
| 253 /** | |
| 254 * @return {string} Application product name to be used in UI. | |
| 255 */ | |
| 256 remoting.Application.Delegate.prototype.getApplicationName = function() {}; | |
| 257 | |
| 258 /** | |
| 259 * Called when a new session has been connected. | |
| 260 * | |
| 261 * @param {remoting.ConnectionInfo} connectionInfo | |
| 262 * @return {void} Nothing. | |
| 263 */ | |
| 264 remoting.Application.Delegate.prototype.handleConnected = function( | |
| 265 connectionInfo) {}; | |
| 266 | |
| 267 /** | |
| 268 * Called when the current session has been disconnected. | |
| 269 * | |
| 270 * @return {void} Nothing. | |
| 271 */ | |
| 272 remoting.Application.Delegate.prototype.handleDisconnected = function() {}; | |
| 273 | |
| 274 /** | |
| 275 * Called when the current session's connection has failed. | |
| 276 * | |
| 277 * @param {remoting.SessionConnector} connector | |
| 278 * @param {!remoting.Error} error | |
| 279 * @return {void} Nothing. | |
| 280 */ | |
| 281 remoting.Application.Delegate.prototype.handleConnectionFailed = | |
| 282 function(connector, error) {}; | |
| 283 | |
| 284 /** | |
| 285 * Called when an error needs to be displayed to the user. | |
| 286 * | |
| 287 * @param {!remoting.Error} errorTag The error to be localized and displayed. | |
| 288 * @return {void} Nothing. | |
| 289 */ | |
| 290 remoting.Application.Delegate.prototype.handleError = function(errorTag) {}; | |
| 291 | |
| 292 /** | |
| 293 * Perform any application-specific cleanup before exiting. This is called in | |
| 294 * lieu of start() if the user declines the app permissions, and will usually | |
| 295 * be called immediately prior to exiting, although delegates should not rely | |
| 296 * on this. | |
| 297 */ | |
| 298 remoting.Application.Delegate.prototype.handleExit = function() {}; | |
| 299 | |
| 300 | |
| 301 /** @type {remoting.Application} */ | 259 /** @type {remoting.Application} */ |
| 302 remoting.app = null; | 260 remoting.app = null; |
| OLD | NEW |