OLD | NEW |
1 // Copyright 2015 The Chromium Authors. All rights reserved. | 1 // Copyright 2015 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 * Class handling user-facing aspects of the client session. | 7 * Class handling user-facing aspects of the client session. |
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 * True to enable mouse lock. | |
17 * This is currently disabled because the current client plugin does not | |
18 * properly handle mouse lock and delegated large cursors at the same time. | |
19 * This should be re-enabled (by removing this flag) once a version of | |
20 * the plugin that supports both has reached Chrome Stable channel. | |
21 * (crbug.com/429322). | |
22 * | |
23 * @type {boolean} | |
24 */ | |
25 remoting.enableMouseLock = false; | |
26 | |
27 /** | |
28 * @param {remoting.ClientPlugin} plugin | 16 * @param {remoting.ClientPlugin} plugin |
29 * @param {remoting.ClientSession} session | |
30 * @param {HTMLElement} container | 17 * @param {HTMLElement} container |
31 * @param {remoting.Host} host | 18 * @param {remoting.Host} host |
32 * @param {remoting.DesktopConnectedView.Mode} mode The mode of this connection. | 19 * @param {remoting.DesktopConnectedView.Mode} mode The mode of this connection. |
33 * @param {string} defaultRemapKeys The default set of remap keys, to use | 20 * @param {string} defaultRemapKeys The default set of remap keys, to use |
34 * when the client doesn't define any. | 21 * when the client doesn't define any. |
35 * @constructor | 22 * @constructor |
36 * @extends {base.EventSourceImpl} | 23 * @extends {base.EventSourceImpl} |
| 24 * @implements {base.Disposable} |
37 */ | 25 */ |
38 remoting.DesktopConnectedView = function(plugin, session, container, host, mode, | 26 remoting.DesktopConnectedView = function(plugin, container, host, mode, |
39 defaultRemapKeys) { | 27 defaultRemapKeys) { |
40 this.session_ = session; | |
41 | 28 |
42 /** @private {HTMLElement} */ | 29 /** @private {HTMLElement} */ |
43 this.container_ = container; | 30 this.container_ = container; |
44 | 31 |
45 /** @private {remoting.ClientPlugin} */ | 32 /** @private {remoting.ClientPlugin} */ |
46 this.plugin_ = plugin; | 33 this.plugin_ = plugin; |
47 | 34 |
48 /** @private */ | 35 /** @private */ |
49 this.host_ = host; | 36 this.host_ = host; |
50 | 37 |
51 /** @private */ | 38 /** @private */ |
52 this.mode_ = mode; | 39 this.mode_ = mode; |
53 | 40 |
54 /** @private {string} */ | 41 /** @private {string} */ |
55 this.defaultRemapKeys_ = defaultRemapKeys; | 42 this.defaultRemapKeys_ = defaultRemapKeys; |
56 | 43 |
57 /** @private */ | |
58 this.callPluginLostFocus_ = this.pluginLostFocus_.bind(this); | |
59 /** @private */ | |
60 this.callPluginGotFocus_ = this.pluginGotFocus_.bind(this); | |
61 /** @private {Element} */ | 44 /** @private {Element} */ |
62 this.debugRegionContainer_ = | 45 this.debugRegionContainer_ = |
63 this.container_.querySelector('.debug-region-container'); | 46 this.container_.querySelector('.debug-region-container'); |
64 | 47 |
65 /** @private {Element} */ | |
66 this.mouseCursorOverlay_ = | |
67 this.container_.querySelector('.mouse-cursor-overlay'); | |
68 | |
69 /** @private {remoting.DesktopViewport} */ | 48 /** @private {remoting.DesktopViewport} */ |
70 this.viewport_ = null; | 49 this.viewport_ = null; |
71 | 50 |
72 /** @type {Element} */ | 51 /** private {remoting.ConnectedView} */ |
73 var img = this.mouseCursorOverlay_; | 52 this.view_ = null; |
74 /** | |
75 * @param {Event} event | |
76 * @private | |
77 */ | |
78 this.updateMouseCursorPosition_ = function(event) { | |
79 img.style.top = event.offsetY + 'px'; | |
80 img.style.left = event.offsetX + 'px'; | |
81 }; | |
82 | 53 |
83 /** @private {remoting.VideoFrameRecorder} */ | 54 /** @private {remoting.VideoFrameRecorder} */ |
84 this.videoFrameRecorder_ = null; | 55 this.videoFrameRecorder_ = null; |
85 | 56 |
86 /** private {base.Disposable} */ | 57 /** private {base.Disposable} */ |
87 this.eventHooks_ = null; | 58 this.eventHooks_ = null; |
88 | 59 |
89 this.setupPlugin_(); | 60 this.setupPlugin_(); |
90 }; | 61 }; |
91 | 62 |
| 63 /** @return {void} Nothing. */ |
| 64 remoting.DesktopConnectedView.prototype.dispose = function() { |
| 65 if (remoting.windowFrame) { |
| 66 remoting.windowFrame.setDesktopConnectedView(null); |
| 67 } |
| 68 if (remoting.toolbar) { |
| 69 remoting.toolbar.setDesktopConnectedView(null); |
| 70 } |
| 71 if (remoting.optionsMenu) { |
| 72 remoting.optionsMenu.setDesktopConnectedView(null); |
| 73 } |
| 74 |
| 75 document.body.classList.remove('connected'); |
| 76 |
| 77 base.dispose(this.eventHooks_); |
| 78 this.eventHooks_ = null; |
| 79 |
| 80 base.dispose(this.viewport_); |
| 81 this.viewport_ = null; |
| 82 }; |
| 83 |
92 // The mode of this session. | 84 // The mode of this session. |
93 /** @enum {number} */ | 85 /** @enum {number} */ |
94 remoting.DesktopConnectedView.Mode = { | 86 remoting.DesktopConnectedView.Mode = { |
95 IT2ME: 0, | 87 IT2ME: 0, |
96 ME2ME: 1, | 88 ME2ME: 1, |
97 APP_REMOTING: 2 | 89 APP_REMOTING: 2 |
98 }; | 90 }; |
99 | 91 |
100 // Keys for per-host settings. | 92 // Keys for per-host settings. |
101 remoting.DesktopConnectedView.KEY_REMAP_KEYS = 'remapKeys'; | 93 remoting.DesktopConnectedView.KEY_REMAP_KEYS = 'remapKeys'; |
(...skipping 61 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
163 } else if (this.mode_ != remoting.DesktopConnectedView.Mode.ME2ME && | 155 } else if (this.mode_ != remoting.DesktopConnectedView.Mode.ME2ME && |
164 this.mode_ != remoting.DesktopConnectedView.Mode.APP_REMOTING) { | 156 this.mode_ != remoting.DesktopConnectedView.Mode.APP_REMOTING) { |
165 var sendCadElement = document.getElementById('send-ctrl-alt-del'); | 157 var sendCadElement = document.getElementById('send-ctrl-alt-del'); |
166 sendCadElement.hidden = true; | 158 sendCadElement.hidden = true; |
167 } | 159 } |
168 | 160 |
169 // Apply customized key remappings if the plugin supports remapKeys. | 161 // Apply customized key remappings if the plugin supports remapKeys. |
170 if (this.plugin_.hasFeature(remoting.ClientPlugin.Feature.REMAP_KEY)) { | 162 if (this.plugin_.hasFeature(remoting.ClientPlugin.Feature.REMAP_KEY)) { |
171 this.applyRemapKeys_(true); | 163 this.applyRemapKeys_(true); |
172 } | 164 } |
173 | |
174 // TODO(wez): Only allow mouse lock if the app has the pointerLock permission. | |
175 // Enable automatic mouse-lock. | |
176 if (remoting.enableMouseLock && | |
177 this.plugin_.hasFeature(remoting.ClientPlugin.Feature.ALLOW_MOUSE_LOCK)) { | |
178 this.plugin_.allowMouseLock(); | |
179 } | |
180 | |
181 this.plugin_.setMouseCursorHandler(this.updateMouseCursorImage_.bind(this)); | |
182 }; | 165 }; |
183 | 166 |
184 /** | 167 /** |
185 * This is a callback that gets called when the window is resized. | 168 * This is a callback that gets called when the window is resized. |
186 * | 169 * |
187 * @return {void} Nothing. | 170 * @return {void} Nothing. |
188 * @private. | 171 * @private. |
189 */ | 172 */ |
190 remoting.DesktopConnectedView.prototype.onResize_ = function() { | 173 remoting.DesktopConnectedView.prototype.onResize_ = function() { |
191 if (this.viewport_) { | 174 if (this.viewport_) { |
192 this.viewport_.onResize(); | 175 this.viewport_.onResize(); |
193 } | 176 } |
194 }; | 177 }; |
195 | 178 |
196 /** | 179 /** |
197 * Called when the app window is hidden. | |
198 * @return {void} Nothing. | |
199 */ | |
200 remoting.DesktopConnectedView.prototype.onVisibilityChanged_ = function() { | |
201 this.pauseVideo(document.hidden); | |
202 }; | |
203 | |
204 /** | |
205 * Callback that the plugin invokes to indicate when the connection is | 180 * Callback that the plugin invokes to indicate when the connection is |
206 * ready. | 181 * ready. |
207 * | 182 * |
208 * @param {boolean} ready True if the connection is ready. | 183 * @param {boolean} ready True if the connection is ready. |
209 */ | 184 */ |
210 remoting.DesktopConnectedView.prototype.onConnectionReady = function(ready) { | 185 remoting.DesktopConnectedView.prototype.onConnectionReady = function(ready) { |
211 if (!ready) { | 186 if (this.view_) { |
212 this.container_.classList.add('session-client-inactive'); | 187 this.view_.onConnectionReady(ready); |
213 } else { | |
214 this.container_.classList.remove('session-client-inactive'); | |
215 } | 188 } |
216 }; | 189 }; |
217 | 190 |
218 /** | 191 remoting.DesktopConnectedView.prototype.onConnected = function() { |
219 * Deletes the <embed> element from the container, without sending a | 192 document.body.classList.add('connected'); |
220 * session_terminate request. This is to be called when the session was | 193 |
221 * disconnected by the Host. | 194 this.view_ = new remoting.ConnectedView( |
222 * | 195 this.plugin_, this.container_, |
223 * @return {void} Nothing. | 196 this.container_.querySelector('.mouse-cursor-overlay')); |
224 */ | 197 |
225 remoting.DesktopConnectedView.prototype.removePlugin = function() { | 198 var scrollerElement = document.getElementById('scroller'); |
226 if (this.plugin_) { | 199 this.viewport_ = new remoting.DesktopViewport( |
227 this.plugin_.element().removeEventListener( | 200 scrollerElement || document.body, |
228 'focus', this.callPluginGotFocus_, false); | 201 this.plugin_.hostDesktop(), |
229 this.plugin_.element().removeEventListener( | 202 this.host_.options); |
230 'blur', this.callPluginLostFocus_, false); | 203 |
231 this.plugin_ = null; | 204 if (remoting.windowFrame) { |
| 205 remoting.windowFrame.setDesktopConnectedView(this); |
| 206 } |
| 207 if (remoting.toolbar) { |
| 208 remoting.toolbar.setDesktopConnectedView(this); |
| 209 } |
| 210 if (remoting.optionsMenu) { |
| 211 remoting.optionsMenu.setDesktopConnectedView(this); |
232 } | 212 } |
233 | 213 |
234 this.updateClientSessionUi_(null); | 214 // Activate full-screen related UX. |
| 215 this.eventHooks_ = new base.Disposables( |
| 216 this.view_, |
| 217 new base.DomEventHook(window, 'resize', this.onResize_.bind(this), false), |
| 218 new remoting.Fullscreen.EventHook(this.onFullScreenChanged_.bind(this)) |
| 219 ); |
| 220 this.onFullScreenChanged_(remoting.fullscreen.isActive()); |
235 }; | 221 }; |
236 | 222 |
237 /** | 223 /** |
238 * @param {remoting.ClientSession} clientSession The active session, or null if | |
239 * there is no connection. | |
240 */ | |
241 remoting.DesktopConnectedView.prototype.updateClientSessionUi_ = function( | |
242 clientSession) { | |
243 if (clientSession === null) { | |
244 if (remoting.windowFrame) { | |
245 remoting.windowFrame.setDesktopConnectedView(null); | |
246 } | |
247 if (remoting.toolbar) { | |
248 remoting.toolbar.setDesktopConnectedView(null); | |
249 } | |
250 if (remoting.optionsMenu) { | |
251 remoting.optionsMenu.setDesktopConnectedView(null); | |
252 } | |
253 | |
254 document.body.classList.remove('connected'); | |
255 this.container_.removeEventListener( | |
256 'mousemove', this.updateMouseCursorPosition_, true); | |
257 base.dispose(this.eventHooks_); | |
258 this.eventHooks_ = null; | |
259 base.dispose(this.viewport_); | |
260 this.viewport_ = null; | |
261 } else { | |
262 var scrollerElement = document.getElementById('scroller'); | |
263 this.viewport_ = new remoting.DesktopViewport( | |
264 scrollerElement || document.body, | |
265 this.plugin_.hostDesktop(), | |
266 this.host_.options); | |
267 if (remoting.windowFrame) { | |
268 remoting.windowFrame.setDesktopConnectedView(this); | |
269 } | |
270 if (remoting.toolbar) { | |
271 remoting.toolbar.setDesktopConnectedView(this); | |
272 } | |
273 if (remoting.optionsMenu) { | |
274 remoting.optionsMenu.setDesktopConnectedView(this); | |
275 } | |
276 | |
277 document.body.classList.add('connected'); | |
278 this.container_.addEventListener( | |
279 'mousemove', this.updateMouseCursorPosition_, true); | |
280 // Activate full-screen related UX. | |
281 this.setFocusHandlers_(); | |
282 this.eventHooks_ = new base.Disposables( | |
283 new base.DomEventHook(window, 'resize', this.onResize_.bind(this), false), | |
284 new base.DomEventHook(document, 'visibilitychange', | |
285 this.onVisibilityChanged_.bind(this), false), | |
286 new remoting.Fullscreen.EventHook(this.onFullScreenChanged_.bind(this)) | |
287 ); | |
288 this.onFullScreenChanged_(remoting.fullscreen.isActive()); | |
289 } | |
290 }; | |
291 | |
292 /** | |
293 * Constrains the focus to the plugin element. | |
294 * @private | |
295 */ | |
296 remoting.DesktopConnectedView.prototype.setFocusHandlers_ = function() { | |
297 this.plugin_.element().addEventListener( | |
298 'focus', this.callPluginGotFocus_, false); | |
299 this.plugin_.element().addEventListener( | |
300 'blur', this.callPluginLostFocus_, false); | |
301 this.plugin_.element().focus(); | |
302 }; | |
303 | |
304 /** | |
305 * Set the shrink-to-fit and resize-to-client flags and save them if this is | 224 * Set the shrink-to-fit and resize-to-client flags and save them if this is |
306 * a Me2Me connection. | 225 * a Me2Me connection. |
307 * | 226 * |
308 * @param {boolean} shrinkToFit True if the remote desktop should be scaled | 227 * @param {boolean} shrinkToFit True if the remote desktop should be scaled |
309 * down if it is larger than the client window; false if scroll-bars | 228 * down if it is larger than the client window; false if scroll-bars |
310 * should be added in this case. | 229 * should be added in this case. |
311 * @param {boolean} resizeToClient True if window resizes should cause the | 230 * @param {boolean} resizeToClient True if window resizes should cause the |
312 * host to attempt to resize its desktop to match the client window size; | 231 * host to attempt to resize its desktop to match the client window size; |
313 * false to disable this behaviour for subsequent window resizes--the | 232 * false to disable this behaviour for subsequent window resizes--the |
314 * current host desktop size is not restored in this case. | 233 * current host desktop size is not restored in this case. |
(...skipping 19 matching lines...) Expand all Loading... |
334 // Fullscreen.isActive call is not guaranteed to return true until the | 253 // Fullscreen.isActive call is not guaranteed to return true until the |
335 // full-screen event is triggered. In apps v2, the size of the window's | 254 // full-screen event is triggered. In apps v2, the size of the window's |
336 // client area is calculated differently in full-screen mode, so register | 255 // client area is calculated differently in full-screen mode, so register |
337 // for both events. | 256 // for both events. |
338 this.viewport_.onResize(); | 257 this.viewport_.onResize(); |
339 this.viewport_.enableBumpScroll(Boolean(fullscreen)); | 258 this.viewport_.enableBumpScroll(Boolean(fullscreen)); |
340 } | 259 } |
341 }; | 260 }; |
342 | 261 |
343 /** | 262 /** |
344 * Callback function called when the plugin element gets focus. | |
345 */ | |
346 remoting.DesktopConnectedView.prototype.pluginGotFocus_ = function() { | |
347 remoting.clipboard.initiateToHost(); | |
348 }; | |
349 | |
350 /** | |
351 * Callback function called when the plugin element loses focus. | |
352 */ | |
353 remoting.DesktopConnectedView.prototype.pluginLostFocus_ = function() { | |
354 if (this.plugin_) { | |
355 // Release all keys to prevent them becoming 'stuck down' on the host. | |
356 this.plugin_.releaseAllKeys(); | |
357 if (this.plugin_.element()) { | |
358 // Focus should stay on the element, not (for example) the toolbar. | |
359 // Due to crbug.com/246335, we can't restore the focus immediately, | |
360 // otherwise the plugin gets confused about whether or not it has focus. | |
361 window.setTimeout( | |
362 this.plugin_.element().focus.bind(this.plugin_.element()), 0); | |
363 } | |
364 } | |
365 }; | |
366 | |
367 /** | |
368 * @param {string} url | |
369 * @param {number} hotspotX | |
370 * @param {number} hotspotY | |
371 */ | |
372 remoting.DesktopConnectedView.prototype.updateMouseCursorImage_ = | |
373 function(url, hotspotX, hotspotY) { | |
374 this.mouseCursorOverlay_.hidden = !url; | |
375 if (url) { | |
376 this.mouseCursorOverlay_.style.marginLeft = '-' + hotspotX + 'px'; | |
377 this.mouseCursorOverlay_.style.marginTop = '-' + hotspotY + 'px'; | |
378 this.mouseCursorOverlay_.src = url; | |
379 } | |
380 }; | |
381 | |
382 /** | |
383 * Sets and stores the key remapping setting for the current host. | 263 * Sets and stores the key remapping setting for the current host. |
384 * | 264 * |
385 * @param {string} remappings Comma separated list of key remappings. | 265 * @param {string} remappings Comma separated list of key remappings. |
386 */ | 266 */ |
387 remoting.DesktopConnectedView.prototype.setRemapKeys = function(remappings) { | 267 remoting.DesktopConnectedView.prototype.setRemapKeys = function(remappings) { |
388 // Cancel any existing remappings and apply the new ones. | 268 // Cancel any existing remappings and apply the new ones. |
389 this.applyRemapKeys_(false); | 269 this.applyRemapKeys_(false); |
390 this.host_.options.remapKeys = remappings; | 270 this.host_.options.remapKeys = remappings; |
391 this.applyRemapKeys_(true); | 271 this.applyRemapKeys_(true); |
392 | 272 |
(...skipping 69 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
462 /** | 342 /** |
463 * Sends a Print Screen keypress to the remoting client. | 343 * Sends a Print Screen keypress to the remoting client. |
464 * | 344 * |
465 * @return {void} Nothing. | 345 * @return {void} Nothing. |
466 */ | 346 */ |
467 remoting.DesktopConnectedView.prototype.sendPrintScreen = function() { | 347 remoting.DesktopConnectedView.prototype.sendPrintScreen = function() { |
468 console.log('Sending Print Screen.'); | 348 console.log('Sending Print Screen.'); |
469 this.sendKeyCombination_([0x070046]); | 349 this.sendKeyCombination_([0x070046]); |
470 }; | 350 }; |
471 | 351 |
472 /** | |
473 * Requests that the host pause or resume video updates. | |
474 * | |
475 * @param {boolean} pause True to pause video, false to resume. | |
476 * @return {void} Nothing. | |
477 */ | |
478 remoting.DesktopConnectedView.prototype.pauseVideo = function(pause) { | |
479 if (this.plugin_) { | |
480 this.plugin_.pauseVideo(pause); | |
481 } | |
482 }; | |
483 | |
484 /** | |
485 * Requests that the host pause or resume audio. | |
486 * | |
487 * @param {boolean} pause True to pause audio, false to resume. | |
488 * @return {void} Nothing. | |
489 */ | |
490 remoting.DesktopConnectedView.prototype.pauseAudio = function(pause) { | |
491 if (this.plugin_) { | |
492 this.plugin_.pauseAudio(pause) | |
493 } | |
494 }; | |
495 | |
496 remoting.DesktopConnectedView.prototype.initVideoFrameRecorder = function() { | 352 remoting.DesktopConnectedView.prototype.initVideoFrameRecorder = function() { |
497 this.videoFrameRecorder_ = new remoting.VideoFrameRecorder(this.plugin_); | 353 this.videoFrameRecorder_ = new remoting.VideoFrameRecorder(this.plugin_); |
498 }; | 354 }; |
499 | 355 |
500 /** | 356 /** |
501 * Returns true if the ClientSession can record video frames to a file. | 357 * Returns true if the ClientSession can record video frames to a file. |
502 * @return {boolean} | 358 * @return {boolean} |
503 */ | 359 */ |
504 remoting.DesktopConnectedView.prototype.canRecordVideo = function() { | 360 remoting.DesktopConnectedView.prototype.canRecordVideo = function() { |
505 return !!this.videoFrameRecorder_; | 361 return !!this.videoFrameRecorder_; |
(...skipping 50 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
556 var rect = document.createElement('div'); | 412 var rect = document.createElement('div'); |
557 rect.classList.add('debug-region-rect'); | 413 rect.classList.add('debug-region-rect'); |
558 rect.style.left = rects[i][0] + 'px'; | 414 rect.style.left = rects[i][0] + 'px'; |
559 rect.style.top = rects[i][1] +'px'; | 415 rect.style.top = rects[i][1] +'px'; |
560 rect.style.width = rects[i][2] +'px'; | 416 rect.style.width = rects[i][2] +'px'; |
561 rect.style.height = rects[i][3] + 'px'; | 417 rect.style.height = rects[i][3] + 'px'; |
562 this.debugRegionContainer_.appendChild(rect); | 418 this.debugRegionContainer_.appendChild(rect); |
563 } | 419 } |
564 } | 420 } |
565 }; | 421 }; |
OLD | NEW |