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 desktop | 7 * This class implements the functionality that is specific to desktop |
8 * remoting ("Chromoting" or CRD). | 8 * remoting ("Chromoting" or CRD). |
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 * @extends {remoting.Application} | |
20 */ | 21 */ |
21 remoting.DesktopRemoting = function(app) { | 22 remoting.DesktopRemoting = function(appCapabilities) { |
22 /** | 23 base.inherits(this, remoting.Application, appCapabilities); |
23 * TODO(garykac): Remove this reference to the Application. It's only | |
24 * needed to get the current mode when reporting errors. So we should be | |
25 * able to refactor and remove this reference cycle. | |
26 * | |
27 * @private {remoting.Application} | |
28 */ | |
29 this.app_ = app; | |
30 app.setDelegate(this); | |
31 | 24 |
32 /** | 25 /** |
33 * Whether to refresh the JID and retry the connection if the current JID | 26 * Whether to refresh the JID and retry the connection if the current JID |
34 * is offline. | 27 * is offline. |
35 * | 28 * |
36 * @private {boolean} | 29 * @private {boolean} |
37 */ | 30 */ |
38 this.refreshHostJidIfOffline_ = true; | 31 this.refreshHostJidIfOffline_ = true; |
39 | 32 |
40 /** @private {remoting.DesktopConnectedView} */ | 33 /** @private {remoting.DesktopConnectedView} */ |
41 this.connectedView_ = null; | 34 this.connectedView_ = null; |
42 | 35 |
43 remoting.desktopDelegateForTesting = this; | 36 remoting.desktopDelegateForTesting = this; |
44 }; | 37 }; |
45 | 38 |
46 /** | 39 /** |
47 * Initialize the application and register all event handlers. After this | 40 * Required for remoting.ApplicationInterface interface. |
Jamie
2015/03/25 20:00:39
I don't think you need this annotation on every me
garykac
2015/03/26 01:41:57
@override {type} works, but the type isn't validat
| |
48 * is called, the app is running and waiting for user events. | |
49 * | 41 * |
50 * @return {void} Nothing. | 42 * @return {string} Application product name to be used in UI. |
43 * @override | |
51 */ | 44 */ |
52 remoting.DesktopRemoting.prototype.init = function() { | 45 remoting.DesktopRemoting.prototype.getApplicationName = function() { |
46 return chrome.i18n.getMessage(/*i18n-content*/'PRODUCT_NAME'); | |
47 }; | |
48 | |
49 /** | |
50 * Required for remoting.ApplicationInterface interface. | |
51 * | |
52 * @param {!remoting.Error} error The failure reason. | |
53 * @override | |
54 */ | |
55 remoting.DesktopRemoting.prototype.signInFailed = function(error) { | |
56 remoting.showErrorMessage(error); | |
57 }; | |
58 | |
59 /** | |
60 * Required for remoting.ApplicationInterface interface. | |
61 * | |
62 * @override | |
63 */ | |
64 remoting.DesktopRemoting.prototype.initApplication = function() { | |
53 remoting.initElementEventHandlers(); | 65 remoting.initElementEventHandlers(); |
54 | 66 |
55 if (base.isAppsV2()) { | 67 if (base.isAppsV2()) { |
56 remoting.windowFrame = new remoting.WindowFrame( | 68 remoting.windowFrame = new remoting.WindowFrame( |
57 document.getElementById('title-bar')); | 69 document.getElementById('title-bar')); |
58 remoting.optionsMenu = remoting.windowFrame.createOptionsMenu(); | 70 remoting.optionsMenu = remoting.windowFrame.createOptionsMenu(); |
59 | 71 |
60 var START_FULLSCREEN = 'start-fullscreen'; | 72 var START_FULLSCREEN = 'start-fullscreen'; |
61 remoting.fullscreen = new remoting.FullscreenAppsV2(); | 73 remoting.fullscreen = new remoting.FullscreenAppsV2(); |
62 remoting.fullscreen.addListener(function(isFullscreen) { | 74 remoting.fullscreen.addListener(function(isFullscreen) { |
(...skipping 52 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
115 document.getElementById('startup-mode-box-it2me').hidden = false; | 127 document.getElementById('startup-mode-box-it2me').hidden = false; |
116 } | 128 } |
117 }; | 129 }; |
118 this.isWindowed_(onIsWindowed); | 130 this.isWindowed_(onIsWindowed); |
119 } | 131 } |
120 | 132 |
121 remoting.ClientPlugin.factory.preloadPlugin(); | 133 remoting.ClientPlugin.factory.preloadPlugin(); |
122 }; | 134 }; |
123 | 135 |
124 /** | 136 /** |
125 * Start the application. Once start() is called, the delegate can assume that | 137 * Required for remoting.ApplicationInterface interface. |
126 * the user has consented to all permissions specified in the manifest. | |
127 * | 138 * |
128 * @param {remoting.SessionConnector} connector | 139 * @param {string} token An OAuth access token. |
129 * @param {string} token An OAuth access token. The delegate should not cache | 140 * @override |
130 * this token, but can assume that it will remain valid during application | |
131 * start-up. | |
132 */ | 141 */ |
133 remoting.DesktopRemoting.prototype.start = function(connector, token) { | 142 remoting.DesktopRemoting.prototype.startApplication = function(token) { |
134 remoting.identity.getEmail().then( | 143 remoting.identity.getEmail().then( |
135 function(/** string */ email) { | 144 function(/** string */ email) { |
136 document.getElementById('current-email').innerText = email; | 145 document.getElementById('current-email').innerText = email; |
137 document.getElementById('get-started-it2me').disabled = false; | 146 document.getElementById('get-started-it2me').disabled = false; |
138 document.getElementById('get-started-me2me').disabled = false; | 147 document.getElementById('get-started-me2me').disabled = false; |
139 }); | 148 }); |
140 }; | 149 }; |
141 | 150 |
142 /** | 151 /** |
143 * Report an authentication error to the user. This is called in lieu of start() | |
144 * if the user cannot be authenticated or if they decline the app permissions. | |
145 * | |
146 * @param {!remoting.Error} error The failure reason. | |
147 */ | |
148 remoting.DesktopRemoting.prototype.signInFailed = function(error) { | |
149 remoting.showErrorMessage(error); | |
150 }; | |
151 | |
152 /** | |
153 * @return {string} Application product name to be used in UI. | |
154 */ | |
155 remoting.DesktopRemoting.prototype.getApplicationName = function() { | |
garykac
2015/03/23 18:44:22
These were moved earlier in the file so to match t
| |
156 return chrome.i18n.getMessage(/*i18n-content*/'PRODUCT_NAME'); | |
157 }; | |
158 | |
159 /** | |
160 * Called when a new session has been connected. | 152 * Called when a new session has been connected. |
161 * | 153 * |
162 * @param {remoting.ConnectionInfo} connectionInfo | 154 * @param {remoting.ConnectionInfo} connectionInfo |
163 * @return {void} Nothing. | 155 * @return {void} Nothing. |
164 */ | 156 */ |
165 remoting.DesktopRemoting.prototype.handleConnected = function(connectionInfo) { | 157 remoting.DesktopRemoting.prototype.onConnected = function(connectionInfo) { |
158 remoting.Application.prototype.onConnected.call(this, connectionInfo); | |
159 | |
166 // Set the text on the buttons shown under the error message so that they are | 160 // Set the text on the buttons shown under the error message so that they are |
167 // easy to understand in the case where a successful connection failed, as | 161 // easy to understand in the case where a successful connection failed, as |
168 // opposed to the case where a connection never succeeded. | 162 // opposed to the case where a connection never succeeded. |
169 // TODO(garykac): Investigate to see if these need to be reverted to their | 163 // TODO(garykac): Investigate to see if these need to be reverted to their |
170 // original values in the onDisconnected method. | 164 // original values in the onDisconnected method. |
171 var button1 = document.getElementById('client-reconnect-button'); | 165 var button1 = document.getElementById('client-reconnect-button'); |
172 l10n.localizeElementFromTag(button1, /*i18n-content*/'RECONNECT'); | 166 l10n.localizeElementFromTag(button1, /*i18n-content*/'RECONNECT'); |
173 button1.removeAttribute('autofocus'); | 167 button1.removeAttribute('autofocus'); |
174 var button2 = document.getElementById('client-finished-me2me-button'); | 168 var button2 = document.getElementById('client-finished-me2me-button'); |
175 l10n.localizeElementFromTag(button2, /*i18n-content*/'OK'); | 169 l10n.localizeElementFromTag(button2, /*i18n-content*/'OK'); |
(...skipping 12 matching lines...) Expand all Loading... | |
188 this.connectedView_ = new remoting.DesktopConnectedView( | 182 this.connectedView_ = new remoting.DesktopConnectedView( |
189 document.getElementById('client-container'), connectionInfo); | 183 document.getElementById('client-container'), connectionInfo); |
190 | 184 |
191 // By default, under ChromeOS, remap the right Control key to the right | 185 // By default, under ChromeOS, remap the right Control key to the right |
192 // Win / Cmd key. | 186 // Win / Cmd key. |
193 if (remoting.platformIsChromeOS()) { | 187 if (remoting.platformIsChromeOS()) { |
194 connectionInfo.plugin().setRemapKeys('0x0700e4>0x0700e7'); | 188 connectionInfo.plugin().setRemapKeys('0x0700e4>0x0700e7'); |
195 } | 189 } |
196 | 190 |
197 if (connectionInfo.mode() === remoting.DesktopConnectedView.Mode.ME2ME) { | 191 if (connectionInfo.mode() === remoting.DesktopConnectedView.Mode.ME2ME) { |
198 var sessionConnector = remoting.app.getSessionConnector(); | |
199 if (remoting.app.hasCapability(remoting.ClientSession.Capability.CAST)) { | 192 if (remoting.app.hasCapability(remoting.ClientSession.Capability.CAST)) { |
200 sessionConnector.registerProtocolExtension( | 193 this.sessionConnector_.registerProtocolExtension( |
201 new remoting.CastExtensionHandler()); | 194 new remoting.CastExtensionHandler()); |
202 } | 195 } |
203 sessionConnector.registerProtocolExtension( | 196 this.sessionConnector_.registerProtocolExtension( |
204 new remoting.GnubbyAuthHandler()); | 197 new remoting.GnubbyAuthHandler()); |
205 } | 198 } |
206 | 199 |
207 if (remoting.pairingRequested) { | 200 if (remoting.pairingRequested) { |
201 var that = this; | |
208 /** | 202 /** |
209 * @param {string} clientId | 203 * @param {string} clientId |
210 * @param {string} sharedSecret | 204 * @param {string} sharedSecret |
211 */ | 205 */ |
212 var onPairingComplete = function(clientId, sharedSecret) { | 206 var onPairingComplete = function(clientId, sharedSecret) { |
213 var connector = remoting.app.getSessionConnector(); | 207 var connector = that.sessionConnector_; |
214 var host = remoting.hostList.getHostForId(connector.getHostId()); | 208 var host = remoting.hostList.getHostForId(connector.getHostId()); |
215 host.options.pairingInfo.clientId = clientId; | 209 host.options.pairingInfo.clientId = clientId; |
216 host.options.pairingInfo.sharedSecret = sharedSecret; | 210 host.options.pairingInfo.sharedSecret = sharedSecret; |
217 host.options.save(); | 211 host.options.save(); |
218 connector.updatePairingInfo(clientId, sharedSecret); | 212 connector.updatePairingInfo(clientId, sharedSecret); |
219 }; | 213 }; |
220 // Use the platform name as a proxy for the local computer name. | 214 // Use the platform name as a proxy for the local computer name. |
221 // TODO(jamiewalch): Use a descriptive name for the local computer, for | 215 // TODO(jamiewalch): Use a descriptive name for the local computer, for |
222 // example, its Chrome Sync name. | 216 // example, its Chrome Sync name. |
223 var clientName = ''; | 217 var clientName = ''; |
(...skipping 11 matching lines...) Expand all Loading... | |
235 } | 229 } |
236 connectionInfo.session().requestPairing(clientName, onPairingComplete); | 230 connectionInfo.session().requestPairing(clientName, onPairingComplete); |
237 } | 231 } |
238 }; | 232 }; |
239 | 233 |
240 /** | 234 /** |
241 * Called when the current session has been disconnected. | 235 * Called when the current session has been disconnected. |
242 * | 236 * |
243 * @return {void} Nothing. | 237 * @return {void} Nothing. |
244 */ | 238 */ |
245 remoting.DesktopRemoting.prototype.handleDisconnected = function() { | 239 remoting.DesktopRemoting.prototype.onDisconnected = function() { |
240 remoting.Application.prototype.onDisconnected.call(this); | |
241 | |
246 var mode = this.connectedView_.getMode(); | 242 var mode = this.connectedView_.getMode(); |
247 if (mode === remoting.DesktopConnectedView.Mode.IT2ME) { | 243 if (mode === remoting.DesktopConnectedView.Mode.IT2ME) { |
248 remoting.setMode(remoting.AppMode.CLIENT_SESSION_FINISHED_IT2ME); | 244 remoting.setMode(remoting.AppMode.CLIENT_SESSION_FINISHED_IT2ME); |
249 } else { | 245 } else { |
250 remoting.setMode(remoting.AppMode.CLIENT_SESSION_FINISHED_ME2ME); | 246 remoting.setMode(remoting.AppMode.CLIENT_SESSION_FINISHED_ME2ME); |
251 } | 247 } |
252 base.dispose(this.connectedView_); | 248 base.dispose(this.connectedView_); |
253 this.connectedView_ = null; | 249 this.connectedView_ = null; |
254 }; | 250 }; |
255 | 251 |
256 /** | 252 /** |
257 * Called when the current session's connection has failed. | 253 * Called when the current session's connection has failed. |
258 * | 254 * |
259 * @param {remoting.SessionConnector} connector | |
260 * @param {!remoting.Error} error | 255 * @param {!remoting.Error} error |
261 * @return {void} Nothing. | 256 * @return {void} Nothing. |
262 */ | 257 */ |
263 remoting.DesktopRemoting.prototype.handleConnectionFailed = function( | 258 remoting.DesktopRemoting.prototype.onConnectionFailed = function(error) { |
264 connector, error) { | 259 remoting.Application.prototype.onConnectionFailed.call(this, error); |
260 | |
265 var that = this; | 261 var that = this; |
266 var onHostListRefresh = function(/** boolean */ success) { | 262 var onHostListRefresh = function(/** boolean */ success) { |
267 if (success) { | 263 if (success) { |
264 var connector = that.sessionConnector_; | |
268 var host = remoting.hostList.getHostForId(connector.getHostId()); | 265 var host = remoting.hostList.getHostForId(connector.getHostId()); |
269 if (host) { | 266 if (host) { |
270 connector.retryConnectMe2Me(host); | 267 connector.retryConnectMe2Me(host); |
271 return; | 268 return; |
272 } | 269 } |
273 } | 270 } |
274 that.handleError(error); | 271 that.onError(error); |
275 }; | 272 }; |
276 | 273 |
277 var mode = this.app_.getSessionConnector().getConnectionMode(); | 274 var mode = this.sessionConnector_.getConnectionMode(); |
278 if (error.hasTag(remoting.Error.Tag.HOST_IS_OFFLINE) && | 275 if (error.hasTag(remoting.Error.Tag.HOST_IS_OFFLINE) && |
279 mode === remoting.DesktopConnectedView.Mode.ME2ME && | 276 mode === remoting.DesktopConnectedView.Mode.ME2ME && |
280 this.refreshHostJidIfOffline_) { | 277 this.refreshHostJidIfOffline_) { |
281 this.refreshHostJidIfOffline_ = false; | 278 this.refreshHostJidIfOffline_ = false; |
282 | 279 |
283 // The plugin will be re-created when the host finished refreshing | 280 // The plugin will be re-created when the host finished refreshing |
284 remoting.hostList.refresh(onHostListRefresh); | 281 remoting.hostList.refresh(onHostListRefresh); |
285 } else { | 282 } else { |
286 this.handleError(error); | 283 this.onError(error); |
287 } | 284 } |
288 }; | 285 }; |
289 | 286 |
290 /** | 287 /** |
291 * Called when an error needs to be displayed to the user. | 288 * Called when an error needs to be displayed to the user. |
292 * | 289 * |
293 * @param {!remoting.Error} error The error to be localized and displayed. | 290 * @param {!remoting.Error} error The error to be localized and displayed. |
294 * @return {void} Nothing. | 291 * @return {void} Nothing. |
295 */ | 292 */ |
296 remoting.DesktopRemoting.prototype.handleError = function(error) { | 293 remoting.DesktopRemoting.prototype.onError = function(error) { |
294 remoting.Application.prototype.onError.call(this, error); | |
295 | |
297 console.error('Connection failed: ' + error.toString()); | 296 console.error('Connection failed: ' + error.toString()); |
298 var mode = this.connectedView_ ? this.connectedView_.getMode() | 297 var mode = this.connectedView_ ? this.connectedView_.getMode() |
299 : this.app_.getSessionConnector().getConnectionMode(); | 298 : this.sessionConnector_.getConnectionMode(); |
300 base.dispose(this.connectedView_); | 299 base.dispose(this.connectedView_); |
301 this.connectedView_ = null; | 300 this.connectedView_ = null; |
302 | 301 |
303 if (error.hasTag(remoting.Error.Tag.AUTHENTICATION_FAILED)) { | 302 if (error.hasTag(remoting.Error.Tag.AUTHENTICATION_FAILED)) { |
304 remoting.setMode(remoting.AppMode.HOME); | 303 remoting.setMode(remoting.AppMode.HOME); |
305 remoting.handleAuthFailureAndRelaunch(); | 304 remoting.handleAuthFailureAndRelaunch(); |
306 return; | 305 return; |
307 } | 306 } |
308 | 307 |
309 // Reset the refresh flag so that the next connection will retry if needed. | 308 // Reset the refresh flag so that the next connection will retry if needed. |
310 this.refreshHostJidIfOffline_ = true; | 309 this.refreshHostJidIfOffline_ = true; |
311 | 310 |
312 var errorDiv = document.getElementById('connect-error-message'); | 311 var errorDiv = document.getElementById('connect-error-message'); |
313 l10n.localizeElementFromTag(errorDiv, error.getTag()); | 312 l10n.localizeElementFromTag(errorDiv, error.getTag()); |
314 | 313 |
315 if (mode == remoting.DesktopConnectedView.Mode.IT2ME) { | 314 if (mode == remoting.DesktopConnectedView.Mode.IT2ME) { |
316 remoting.setMode(remoting.AppMode.CLIENT_CONNECT_FAILED_IT2ME); | 315 remoting.setMode(remoting.AppMode.CLIENT_CONNECT_FAILED_IT2ME); |
317 } else { | 316 } else { |
318 remoting.setMode(remoting.AppMode.CLIENT_CONNECT_FAILED_ME2ME); | 317 remoting.setMode(remoting.AppMode.CLIENT_CONNECT_FAILED_ME2ME); |
319 } | 318 } |
320 }; | 319 }; |
321 | 320 |
322 /** | 321 /** |
323 * No cleanup required for desktop remoting. | |
324 */ | |
325 remoting.DesktopRemoting.prototype.handleExit = function() { | |
326 }; | |
327 | |
328 /** | |
329 * Determine whether or not the app is running in a window. | 322 * Determine whether or not the app is running in a window. |
330 * @param {function(boolean):void} callback Callback to receive whether or not | 323 * @param {function(boolean):void} callback Callback to receive whether or not |
331 * the current tab is running in windowed mode. | 324 * the current tab is running in windowed mode. |
332 * @private | 325 * @private |
333 */ | 326 */ |
334 remoting.DesktopRemoting.prototype.isWindowed_ = function(callback) { | 327 remoting.DesktopRemoting.prototype.isWindowed_ = function(callback) { |
335 /** @param {chrome.Window} win The current window. */ | 328 /** @param {chrome.Window} win The current window. */ |
336 var windowCallback = function(win) { | 329 var windowCallback = function(win) { |
337 callback(win.type == 'popup'); | 330 callback(win.type == 'popup'); |
338 }; | 331 }; |
(...skipping 12 matching lines...) Expand all Loading... | |
351 } | 344 } |
352 } | 345 } |
353 | 346 |
354 /** | 347 /** |
355 * If an IT2Me client or host is active then prompt the user before closing. | 348 * If an IT2Me client or host is active then prompt the user before closing. |
356 * If a Me2Me client is active then don't bother, since closing the window is | 349 * If a Me2Me client is active then don't bother, since closing the window is |
357 * the more intuitive way to end a Me2Me session, and re-connecting is easy. | 350 * the more intuitive way to end a Me2Me session, and re-connecting is easy. |
358 * @private | 351 * @private |
359 */ | 352 */ |
360 remoting.DesktopRemoting.prototype.promptClose_ = function() { | 353 remoting.DesktopRemoting.prototype.promptClose_ = function() { |
361 var sessionConnector = remoting.app.getSessionConnector(); | 354 var sessionConnector = this.sessionConnector_; |
362 if (sessionConnector && | 355 if (sessionConnector && |
363 sessionConnector.getConnectionMode() == | 356 sessionConnector.getConnectionMode() == |
364 remoting.DesktopConnectedView.Mode.IT2ME) { | 357 remoting.DesktopConnectedView.Mode.IT2ME) { |
365 switch (remoting.currentMode) { | 358 switch (remoting.currentMode) { |
366 case remoting.AppMode.CLIENT_CONNECTING: | 359 case remoting.AppMode.CLIENT_CONNECTING: |
367 case remoting.AppMode.HOST_WAITING_FOR_CODE: | 360 case remoting.AppMode.HOST_WAITING_FOR_CODE: |
368 case remoting.AppMode.HOST_WAITING_FOR_CONNECTION: | 361 case remoting.AppMode.HOST_WAITING_FOR_CONNECTION: |
369 case remoting.AppMode.HOST_SHARED: | 362 case remoting.AppMode.HOST_SHARED: |
370 case remoting.AppMode.IN_SESSION: | 363 case remoting.AppMode.IN_SESSION: |
371 return chrome.i18n.getMessage(/*i18n-content*/'CLOSE_PROMPT'); | 364 return chrome.i18n.getMessage(/*i18n-content*/'CLOSE_PROMPT'); |
372 default: | 365 default: |
373 return null; | 366 return null; |
374 } | 367 } |
375 } | 368 } |
376 }; | 369 }; |
377 | 370 |
378 /** @returns {remoting.DesktopConnectedView} */ | 371 /** @returns {remoting.DesktopConnectedView} */ |
379 remoting.DesktopRemoting.prototype.getConnectedViewForTesting = function() { | 372 remoting.DesktopRemoting.prototype.getConnectedViewForTesting = function() { |
380 return this.connectedView_; | 373 return this.connectedView_; |
381 }; | 374 }; |
382 | |
383 /** | |
384 * Global instance of remoting.DesktopRemoting used for testing. | |
385 * @type {remoting.DesktopRemoting} | |
386 */ | |
387 remoting.desktopDelegateForTesting = null; | |
garykac
2015/03/23 18:44:22
No need for this anymore. We can simply use remoti
| |
OLD | NEW |