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 * This class implements the functionality that is specific to application | 7 * This class implements the functionality that is specific to application |
| 8 * remoting ("AppRemoting" or AR). | 8 * remoting ("AppRemoting" or AR). |
| 9 */ | 9 */ |
| 10 | 10 |
| 11 'use strict'; | 11 'use strict'; |
| 12 | 12 |
| 13 /** @suppress {duplicate} */ | 13 /** @suppress {duplicate} */ |
| 14 var remoting = remoting || {}; | 14 var remoting = remoting || {}; |
| 15 | 15 |
| 16 /** | 16 /** |
| 17 * @param {remoting.Application} app The main app that owns this delegate. | 17 * @param {Array<string>} appCapabilities Array of application capabilities. |
| 18 * @constructor | 18 * @constructor |
| 19 * @implements {remoting.Application.Delegate} | 19 * @implements {remoting.ApplicationInterface} |
| 20 * @implements {remoting.ProtocolExtension} | 20 * @implements {remoting.ProtocolExtension} |
| 21 * @extends {remoting.Application} | |
| 21 */ | 22 */ |
| 22 remoting.AppRemoting = function(app) { | 23 remoting.AppRemoting = function(appCapabilities) { |
| 23 app.setDelegate(this); | 24 base.inherits(this, remoting.Application, appCapabilities); |
| 24 | 25 |
| 25 /** @private {remoting.ApplicationContextMenu} */ | 26 /** @private {remoting.ApplicationContextMenu} */ |
| 26 this.contextMenu_ = null; | 27 this.contextMenu_ = null; |
| 27 | 28 |
| 28 /** @private {remoting.KeyboardLayoutsMenu} */ | 29 /** @private {remoting.KeyboardLayoutsMenu} */ |
| 29 this.keyboardLayoutsMenu_ = null; | 30 this.keyboardLayoutsMenu_ = null; |
| 30 | 31 |
| 31 /** @private {remoting.WindowActivationMenu} */ | 32 /** @private {remoting.WindowActivationMenu} */ |
| 32 this.windowActivationMenu_ = null; | 33 this.windowActivationMenu_ = null; |
| 33 | 34 |
| (...skipping 18 matching lines...) Expand all Loading... | |
| 52 this.sharedSecret = ''; | 53 this.sharedSecret = ''; |
| 53 | 54 |
| 54 this.host = { | 55 this.host = { |
| 55 /** @type {string} */ | 56 /** @type {string} */ |
| 56 applicationId: '', | 57 applicationId: '', |
| 57 | 58 |
| 58 /** @type {string} */ | 59 /** @type {string} */ |
| 59 hostId: ''}; | 60 hostId: ''}; |
| 60 }; | 61 }; |
| 61 | 62 |
| 63 /** @return {string} */ | |
| 64 remoting.AppRemoting.prototype.runApplicationUrl = function() { | |
|
Jamie
2015/03/25 20:00:39
This shouldn't be a public member, I don't think.
garykac
2015/03/26 01:41:57
Done.
| |
| 65 return remoting.settings.APP_REMOTING_API_BASE_URL + '/applications/' + | |
| 66 remoting.settings.getAppRemotingApplicationId() + '/run'; | |
| 67 }; | |
| 68 | |
| 62 /** | 69 /** |
| 63 * Initialize the application. This is called before an OAuth token is requested | 70 * Required for remoting.ApplicationInterface interface. |
| 64 * and should be used for tasks such as initializing the DOM, registering event | 71 * |
| 65 * handlers, etc. | 72 * @return {string} Application product name to be used in UI. |
| 73 * @override | |
| 66 */ | 74 */ |
| 67 remoting.AppRemoting.prototype.init = function() { | 75 remoting.AppRemoting.prototype.getApplicationName = function() { |
| 76 var manifest = chrome.runtime.getManifest(); | |
| 77 return manifest.name; | |
| 78 }; | |
| 79 | |
| 80 /** | |
| 81 * Required for remoting.ApplicationInterface interface. | |
| 82 * | |
| 83 * @param {!remoting.Error} error The failure reason. | |
| 84 * @override | |
| 85 */ | |
| 86 remoting.AppRemoting.prototype.signInFailed = function(error) { | |
| 87 this.onError(error); | |
| 88 }; | |
| 89 | |
| 90 /** | |
| 91 * Required for remoting.ApplicationInterface interface. | |
| 92 * | |
| 93 * @override | |
| 94 */ | |
| 95 remoting.AppRemoting.prototype.initApplication = function() { | |
| 68 // TODO(jamiewalch): Remove ClientSession's dependency on remoting.fullscreen | 96 // TODO(jamiewalch): Remove ClientSession's dependency on remoting.fullscreen |
| 69 // so that this is no longer required. | 97 // so that this is no longer required. |
| 70 remoting.fullscreen = new remoting.FullscreenAppsV2(); | 98 remoting.fullscreen = new remoting.FullscreenAppsV2(); |
| 71 | 99 |
| 72 var restoreHostWindows = function() { | 100 var restoreHostWindows = function() { |
| 73 if (remoting.clientSession) { | 101 if (remoting.clientSession) { |
| 74 remoting.clientSession.sendClientMessage('restoreAllWindows', ''); | 102 remoting.clientSession.sendClientMessage('restoreAllWindows', ''); |
| 75 } | 103 } |
| 76 }; | 104 }; |
| 77 chrome.app.window.current().onRestored.addListener(restoreHostWindows); | 105 chrome.app.window.current().onRestored.addListener(restoreHostWindows); |
| 78 | 106 |
| 79 remoting.windowShape.updateClientWindowShape(); | 107 remoting.windowShape.updateClientWindowShape(); |
| 80 | 108 |
| 81 // Initialize the context menus. | 109 // Initialize the context menus. |
| 82 if (remoting.platformIsChromeOS()) { | 110 if (remoting.platformIsChromeOS()) { |
| 83 var adapter = new remoting.ContextMenuChrome(); | 111 var adapter = new remoting.ContextMenuChrome(); |
| 84 } else { | 112 } else { |
| 85 var adapter = new remoting.ContextMenuDom( | 113 var adapter = new remoting.ContextMenuDom( |
| 86 document.getElementById('context-menu')); | 114 document.getElementById('context-menu')); |
| 87 } | 115 } |
| 88 this.contextMenu_ = new remoting.ApplicationContextMenu(adapter); | 116 this.contextMenu_ = new remoting.ApplicationContextMenu(adapter); |
| 89 this.keyboardLayoutsMenu_ = new remoting.KeyboardLayoutsMenu(adapter); | 117 this.keyboardLayoutsMenu_ = new remoting.KeyboardLayoutsMenu(adapter); |
| 90 this.windowActivationMenu_ = new remoting.WindowActivationMenu(adapter); | 118 this.windowActivationMenu_ = new remoting.WindowActivationMenu(adapter); |
| 91 | 119 |
| 92 remoting.LoadingWindow.show(); | 120 remoting.LoadingWindow.show(); |
| 93 }; | 121 }; |
| 94 | 122 |
| 95 /** | 123 /** |
| 96 * Start the application. Once start() is called, the delegate can assume that | 124 * Required for remoting.ApplicationInterface interface. |
| 97 * the user has consented to all permissions specified in the manifest. | |
| 98 * | 125 * |
| 99 * @param {remoting.SessionConnector} connector | 126 * @param {string} token An OAuth access token. |
| 100 * @param {string} token An OAuth access token. The delegate should not cache | 127 * @override |
| 101 * this token, but can assume that it will remain valid during application | |
| 102 * start-up. | |
| 103 */ | 128 */ |
| 104 remoting.AppRemoting.prototype.start = function(connector, token) { | 129 remoting.AppRemoting.prototype.startApplication = function(token) { |
| 105 /** @type {remoting.AppRemoting} */ | 130 /** @type {remoting.AppRemoting} */ |
| 106 var that = this; | 131 var that = this; |
| 107 | 132 |
| 108 /** @param {!remoting.Xhr.Response} xhrResponse */ | 133 /** @param {!remoting.Xhr.Response} xhrResponse */ |
| 109 var parseAppHostResponse = function(xhrResponse) { | 134 var parseAppHostResponse = function(xhrResponse) { |
| 110 if (xhrResponse.status == 200) { | 135 if (xhrResponse.status == 200) { |
| 111 var response = /** @type {remoting.AppRemoting.AppHostResponse} */ | 136 var response = /** @type {remoting.AppRemoting.AppHostResponse} */ |
| 112 (base.jsonParseSafe(xhrResponse.getText())); | 137 (base.jsonParseSafe(xhrResponse.getText())); |
| 113 if (response && | 138 if (response && |
| 114 response.status && | 139 response.status && |
| (...skipping 25 matching lines...) Expand all Loading... | |
| 140 * @param {function(string, string):void} onThirdPartyTokenFetched | 165 * @param {function(string, string):void} onThirdPartyTokenFetched |
| 141 * Callback. | 166 * Callback. |
| 142 */ | 167 */ |
| 143 var fetchThirdPartyToken = function( | 168 var fetchThirdPartyToken = function( |
| 144 tokenUrl, hostPublicKey, scope, onThirdPartyTokenFetched) { | 169 tokenUrl, hostPublicKey, scope, onThirdPartyTokenFetched) { |
| 145 // Use the authentication tokens returned by the app-remoting server. | 170 // Use the authentication tokens returned by the app-remoting server. |
| 146 onThirdPartyTokenFetched(host['authorizationCode'], | 171 onThirdPartyTokenFetched(host['authorizationCode'], |
| 147 host['sharedSecret']); | 172 host['sharedSecret']); |
| 148 }; | 173 }; |
| 149 | 174 |
| 150 connector.connectMe2App(host, fetchThirdPartyToken); | 175 that.sessionConnector_.connectMe2App(host, fetchThirdPartyToken); |
| 151 } else if (response && response.status == 'pending') { | 176 } else if (response && response.status == 'pending') { |
| 152 that.handleError(new remoting.Error( | 177 that.onError(new remoting.Error( |
| 153 remoting.Error.Tag.SERVICE_UNAVAILABLE)); | 178 remoting.Error.Tag.SERVICE_UNAVAILABLE)); |
| 154 } | 179 } |
| 155 } else { | 180 } else { |
| 156 console.error('Invalid "runApplication" response from server.'); | 181 console.error('Invalid "runApplication" response from server.'); |
| 157 // TODO(garykac) Start using remoting.Error.fromHttpStatus once it has | 182 // TODO(garykac) Start using remoting.Error.fromHttpStatus once it has |
| 158 // been updated to properly report 'unknown' errors (rather than | 183 // been updated to properly report 'unknown' errors (rather than |
| 159 // reporting them as AUTHENTICATION_FAILED). | 184 // reporting them as AUTHENTICATION_FAILED). |
| 160 if (xhrResponse.status == 0) { | 185 if (xhrResponse.status == 0) { |
| 161 that.handleError(new remoting.Error( | 186 that.onError(new remoting.Error( |
| 162 remoting.Error.Tag.NETWORK_FAILURE)); | 187 remoting.Error.Tag.NETWORK_FAILURE)); |
| 163 } else if (xhrResponse.status == 401) { | 188 } else if (xhrResponse.status == 401) { |
| 164 that.handleError(new remoting.Error( | 189 that.onError(new remoting.Error( |
| 165 remoting.Error.Tag.AUTHENTICATION_FAILED)); | 190 remoting.Error.Tag.AUTHENTICATION_FAILED)); |
| 166 } else if (xhrResponse.status == 403) { | 191 } else if (xhrResponse.status == 403) { |
| 167 that.handleError(new remoting.Error( | 192 that.onError(new remoting.Error( |
| 168 remoting.Error.Tag.APP_NOT_AUTHORIZED)); | 193 remoting.Error.Tag.APP_NOT_AUTHORIZED)); |
| 169 } else if (xhrResponse.status == 502 || xhrResponse.status == 503) { | 194 } else if (xhrResponse.status == 502 || xhrResponse.status == 503) { |
| 170 that.handleError(new remoting.Error( | 195 that.onError(new remoting.Error( |
| 171 remoting.Error.Tag.SERVICE_UNAVAILABLE)); | 196 remoting.Error.Tag.SERVICE_UNAVAILABLE)); |
| 172 } else { | 197 } else { |
| 173 that.handleError(remoting.Error.unexpected()); | 198 that.onError(remoting.Error.unexpected()); |
| 174 } | 199 } |
| 175 } | 200 } |
| 176 }; | 201 }; |
| 177 | 202 |
| 178 new remoting.Xhr({ | 203 new remoting.Xhr({ |
| 179 method: 'POST', | 204 method: 'POST', |
| 180 url: that.runApplicationUrl(), | 205 url: that.runApplicationUrl(), |
| 181 oauthToken: token | 206 oauthToken: token |
| 182 }).start().then(parseAppHostResponse); | 207 }).start().then(parseAppHostResponse); |
| 183 }; | 208 }; |
| 184 | 209 |
| 185 /** | 210 /** |
| 186 * Report an authentication error to the user. This is called in lieu of start() | 211 * Close the loading window before exiting. |
| 187 * if the user cannot be authenticated or if they decline the app permissions. | |
| 188 * | |
| 189 * @param {!remoting.Error} error The failure reason. | |
| 190 */ | 212 */ |
| 191 remoting.AppRemoting.prototype.signInFailed = function(error) { | 213 remoting.AppRemoting.prototype.exit = function() { |
| 192 this.handleError(error); | 214 remoting.LoadingWindow.close(); |
| 215 | |
| 216 // Call superclass exit() after we've closed all our windows. | |
| 217 remoting.Application.prototype.exit.call(this); | |
| 193 }; | 218 }; |
| 194 | 219 |
| 195 /** | 220 /** |
| 196 * @return {string} Application product name to be used in UI. | |
| 197 */ | |
| 198 remoting.AppRemoting.prototype.getApplicationName = function() { | |
| 199 var manifest = chrome.runtime.getManifest(); | |
| 200 return manifest.name; | |
| 201 }; | |
| 202 | |
| 203 /** @return {string} */ | |
| 204 remoting.AppRemoting.prototype.runApplicationUrl = function() { | |
| 205 return remoting.settings.APP_REMOTING_API_BASE_URL + '/applications/' + | |
| 206 remoting.settings.getAppRemotingApplicationId() + '/run'; | |
| 207 }; | |
| 208 | |
| 209 /** | |
| 210 * Called when a new session has been connected. | 221 * Called when a new session has been connected. |
| 211 * | 222 * |
| 212 * @param {remoting.ConnectionInfo} connectionInfo | 223 * @param {remoting.ConnectionInfo} connectionInfo |
| 213 * @return {void} Nothing. | 224 * @return {void} Nothing. |
| 214 */ | 225 */ |
| 215 remoting.AppRemoting.prototype.handleConnected = function(connectionInfo) { | 226 remoting.AppRemoting.prototype.onConnected = function(connectionInfo) { |
| 227 remoting.Application.prototype.onConnected.call(this, connectionInfo); | |
| 228 | |
| 216 remoting.identity.getUserInfo().then( | 229 remoting.identity.getUserInfo().then( |
| 217 function(userInfo) { | 230 function(userInfo) { |
| 218 remoting.clientSession.sendClientMessage( | 231 remoting.clientSession.sendClientMessage( |
| 219 'setUserDisplayInfo', | 232 'setUserDisplayInfo', |
| 220 JSON.stringify({fullName: userInfo.name})); | 233 JSON.stringify({fullName: userInfo.name})); |
| 221 }); | 234 }); |
| 222 | 235 |
| 223 remoting.app.getSessionConnector().registerProtocolExtension(this); | 236 this.sessionConnector_.registerProtocolExtension(this); |
| 224 | 237 |
| 225 this.connectedView_ = new remoting.AppConnectedView( | 238 this.connectedView_ = new remoting.AppConnectedView( |
| 226 document.getElementById('client-container'), connectionInfo); | 239 document.getElementById('client-container'), connectionInfo); |
| 227 | 240 |
| 228 // Map Cmd to Ctrl on Mac since hosts typically use Ctrl for keyboard | 241 // Map Cmd to Ctrl on Mac since hosts typically use Ctrl for keyboard |
| 229 // shortcuts, but we want them to act as natively as possible. | 242 // shortcuts, but we want them to act as natively as possible. |
| 230 if (remoting.platformIsMac()) { | 243 if (remoting.platformIsMac()) { |
| 231 connectionInfo.plugin().setRemapKeys('0x0700e3>0x0700e0,0x0700e7>0x0700e4'); | 244 connectionInfo.plugin().setRemapKeys('0x0700e3>0x0700e0,0x0700e7>0x0700e4'); |
| 232 } | 245 } |
| 233 }; | 246 }; |
| 234 | 247 |
| 235 /** | 248 /** |
| 236 * Called when the current session has been disconnected. | 249 * Called when the current session has been disconnected. |
| 237 * | 250 * |
| 238 * @return {void} Nothing. | 251 * @return {void} Nothing. |
| 239 */ | 252 */ |
| 240 remoting.AppRemoting.prototype.handleDisconnected = function() { | 253 remoting.AppRemoting.prototype.onDisconnected = function() { |
| 254 remoting.Application.prototype.onDisconnected.call(this); | |
| 255 | |
| 241 base.dispose(this.connectedView_); | 256 base.dispose(this.connectedView_); |
| 242 this.connectedView_ = null; | 257 this.connectedView_ = null; |
| 243 | 258 |
| 244 chrome.app.window.current().close(); | 259 chrome.app.window.current().close(); |
| 245 }; | 260 }; |
| 246 | 261 |
| 247 /** | 262 /** |
| 248 * Called when the current session's connection has failed. | 263 * Called when the current session's connection has failed. |
| 249 * | 264 * |
| 250 * @param {remoting.SessionConnector} connector | |
| 251 * @param {!remoting.Error} error | 265 * @param {!remoting.Error} error |
| 252 * @return {void} Nothing. | 266 * @return {void} Nothing. |
| 253 */ | 267 */ |
| 254 remoting.AppRemoting.prototype.handleConnectionFailed = function( | 268 remoting.AppRemoting.prototype.onConnectionFailed = function(error) { |
| 255 connector, error) { | 269 remoting.Application.prototype.onConnectionFailed.call(this, error); |
| 256 this.handleError(error); | 270 |
| 271 this.onError(error); | |
| 257 }; | 272 }; |
| 258 | 273 |
| 259 /** @return {Array<string>} */ | 274 /** |
| 275 * Called when an error needs to be displayed to the user. | |
| 276 * | |
| 277 * @param {!remoting.Error} error The error to be localized and displayed. | |
| 278 * @return {void} Nothing. | |
| 279 */ | |
| 280 remoting.AppRemoting.prototype.onError = function(error) { | |
| 281 remoting.Application.prototype.onError.call(this, error); | |
| 282 | |
| 283 console.error('Connection failed: ' + error.toString()); | |
| 284 remoting.LoadingWindow.close(); | |
| 285 remoting.MessageWindow.showErrorMessage( | |
| 286 chrome.i18n.getMessage(/*i18n-content*/'CONNECTION_FAILED'), | |
| 287 chrome.i18n.getMessage(error.getTag())); | |
| 288 }; | |
| 289 | |
| 290 | |
| 291 /** | |
| 292 * Required for remoting.ProtocolExtension interface. | |
| 293 * | |
| 294 * @return {Array<string>} | |
| 295 */ | |
| 260 remoting.AppRemoting.prototype.getExtensionTypes = function() { | 296 remoting.AppRemoting.prototype.getExtensionTypes = function() { |
| 261 return ['openURL', 'onWindowRemoved', 'onWindowAdded', | 297 return ['openURL', 'onWindowRemoved', 'onWindowAdded', |
| 262 'onAllWindowsMinimized', 'setKeyboardLayouts', 'pingResponse']; | 298 'onAllWindowsMinimized', 'setKeyboardLayouts', 'pingResponse']; |
| 263 }; | 299 }; |
| 264 | 300 |
| 265 /** | 301 /** |
| 302 * Required for remoting.ProtocolExtension interface. | |
| 303 * | |
| 266 * @param {function(string,string)} sendMessageToHost Callback to send a message | 304 * @param {function(string,string)} sendMessageToHost Callback to send a message |
| 267 * to the host. | 305 * to the host. |
| 268 */ | 306 */ |
| 269 remoting.AppRemoting.prototype.startExtension = function(sendMessageToHost) { | 307 remoting.AppRemoting.prototype.startExtension = function(sendMessageToHost) { |
| 270 }; | 308 }; |
| 271 | 309 |
| 272 /** | 310 /** |
| 311 * Required for remoting.ProtocolExtension interface. | |
| 312 * | |
| 273 * @param {string} type The message type. | 313 * @param {string} type The message type. |
| 274 * @param {Object} message The parsed extension message data. | 314 * @param {Object} message The parsed extension message data. |
| 275 */ | 315 */ |
| 276 remoting.AppRemoting.prototype.onExtensionMessage = function(type, message) { | 316 remoting.AppRemoting.prototype.onExtensionMessage = function(type, message) { |
| 277 switch (type) { | 317 switch (type) { |
| 278 | 318 |
| 279 case 'openURL': | 319 case 'openURL': |
| 280 // URL requests from the hosted app are untrusted, so disallow anything | 320 // URL requests from the hosted app are untrusted, so disallow anything |
| 281 // other than HTTP or HTTPS. | 321 // other than HTTP or HTTPS. |
| 282 var url = base.getStringAttr(message, 'url'); | 322 var url = base.getStringAttr(message, 'url'); |
| (...skipping 29 matching lines...) Expand all Loading... | |
| 312 | 352 |
| 313 case 'pingResponse': | 353 case 'pingResponse': |
| 314 var then = base.getNumberAttr(message, 'timestamp'); | 354 var then = base.getNumberAttr(message, 'timestamp'); |
| 315 var now = new Date().getTime(); | 355 var now = new Date().getTime(); |
| 316 this.contextMenu_.updateConnectionRTT(now - then); | 356 this.contextMenu_.updateConnectionRTT(now - then); |
| 317 return true; | 357 return true; |
| 318 } | 358 } |
| 319 | 359 |
| 320 return false; | 360 return false; |
| 321 }; | 361 }; |
| 322 | |
| 323 /** | |
| 324 * Called when an error needs to be displayed to the user. | |
| 325 * | |
| 326 * @param {!remoting.Error} error The error to be localized and displayed. | |
| 327 * @return {void} Nothing. | |
| 328 */ | |
| 329 remoting.AppRemoting.prototype.handleError = function(error) { | |
| 330 console.error('Connection failed: ' + error.toString()); | |
| 331 remoting.LoadingWindow.close(); | |
| 332 remoting.MessageWindow.showErrorMessage( | |
| 333 chrome.i18n.getMessage(/*i18n-content*/'CONNECTION_FAILED'), | |
| 334 chrome.i18n.getMessage(error.getTag())); | |
| 335 }; | |
| 336 | |
| 337 /** | |
| 338 * Close the loading window before exiting. | |
| 339 */ | |
| 340 remoting.AppRemoting.prototype.handleExit = function() { | |
| 341 remoting.LoadingWindow.close(); | |
| 342 }; | |
| OLD | NEW |