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