Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(185)

Side by Side Diff: remoting/webapp/crd/js/desktop_connected_view.js

Issue 918783002: CRD Viewport Management refactor (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: Reviewer's feedback Created 5 years, 10 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
« no previous file with comments | « remoting/webapp/crd/js/client_session.js ('k') | remoting/webapp/crd/js/desktop_viewport.js » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
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';
(...skipping 49 matching lines...) Expand 10 before | Expand all | Expand 10 after
60 */ 60 */
61 this.onInitialized_ = onInitialized; 61 this.onInitialized_ = onInitialized;
62 62
63 /** @type {function(boolean=):void} @private */ 63 /** @type {function(boolean=):void} @private */
64 this.callOnFullScreenChanged_ = this.onFullScreenChanged_.bind(this) 64 this.callOnFullScreenChanged_ = this.onFullScreenChanged_.bind(this)
65 65
66 /** @private */ 66 /** @private */
67 this.callPluginLostFocus_ = this.pluginLostFocus_.bind(this); 67 this.callPluginLostFocus_ = this.pluginLostFocus_.bind(this);
68 /** @private */ 68 /** @private */
69 this.callPluginGotFocus_ = this.pluginGotFocus_.bind(this); 69 this.callPluginGotFocus_ = this.pluginGotFocus_.bind(this);
70
71 /** @type {number?} @private */
72 this.notifyClientResolutionTimer_ = null;
73
74 /** @type {Element} @private */ 70 /** @type {Element} @private */
75 this.mouseCursorOverlay_ = 71 this.mouseCursorOverlay_ =
76 this.container_.querySelector('.mouse-cursor-overlay'); 72 this.container_.querySelector('.mouse-cursor-overlay');
77 73
74 /** @private {remoting.DesktopViewport} */
75 this.viewport_ = null;
76
78 /** @type {Element} */ 77 /** @type {Element} */
79 var img = this.mouseCursorOverlay_; 78 var img = this.mouseCursorOverlay_;
80 /** @param {Event} event @private */ 79 /** @param {Event} event @private */
81 this.updateMouseCursorPosition_ = function(event) { 80 this.updateMouseCursorPosition_ = function(event) {
82 img.style.top = event.y + 'px'; 81 img.style.top = event.y + 'px';
83 img.style.left = event.x + 'px'; 82 img.style.left = event.x + 'px';
84 }; 83 };
85 84
86 /** @type {number?} @private */
87 this.bumpScrollTimer_ = null;
88
89 // Bump-scroll test variables. Override to use a fake value for the width
90 // and height of the client plugin so that bump-scrolling can be tested
91 // without relying on the actual size of the host desktop.
92 /** @type {number} @private */
93 this.pluginWidthForBumpScrollTesting_ = 0;
94 /** @type {number} @private */
95 this.pluginHeightForBumpScrollTesting_ = 0;
96
97 /** @type {remoting.VideoFrameRecorder} @private */ 85 /** @type {remoting.VideoFrameRecorder} @private */
98 this.videoFrameRecorder_ = null; 86 this.videoFrameRecorder_ = null;
99
100 /** @private {base.Disposables} */
101 this.eventHooks_ = null;
102
103 this.defineEvents(base.values(remoting.DesktopConnectedView.Events));
104 };
105
106 base.extend(remoting.DesktopConnectedView, base.EventSourceImpl);
107
108 /** @enum {string} */
109 remoting.DesktopConnectedView.Events = {
110 bumpScrollStarted: 'bumpScrollStarted',
111 bumpScrollStopped: 'bumpScrollStopped'
112 }; 87 };
113 88
114 // The mode of this session. 89 // The mode of this session.
115 /** @enum {number} */ 90 /** @enum {number} */
116 remoting.DesktopConnectedView.Mode = { 91 remoting.DesktopConnectedView.Mode = {
117 IT2ME: 0, 92 IT2ME: 0,
118 ME2ME: 1, 93 ME2ME: 1,
119 APP_REMOTING: 2 94 APP_REMOTING: 2
120 }; 95 };
121 96
(...skipping 16 matching lines...) Expand all
138 * @return {remoting.DesktopConnectedView.Mode} The current state. 113 * @return {remoting.DesktopConnectedView.Mode} The current state.
139 */ 114 */
140 remoting.DesktopConnectedView.prototype.getMode = function() { 115 remoting.DesktopConnectedView.prototype.getMode = function() {
141 return this.mode_; 116 return this.mode_;
142 }; 117 };
143 118
144 /** 119 /**
145 * @return {boolean} True if shrink-to-fit is enabled; false otherwise. 120 * @return {boolean} True if shrink-to-fit is enabled; false otherwise.
146 */ 121 */
147 remoting.DesktopConnectedView.prototype.getShrinkToFit = function() { 122 remoting.DesktopConnectedView.prototype.getShrinkToFit = function() {
148 return this.host_.options.shrinkToFit; 123 if (this.viewport_) {
124 return this.viewport_.getShrinkToFit();
125 }
126 return false;
149 }; 127 };
150 128
151 /** 129 /**
152 * @return {boolean} True if resize-to-client is enabled; false otherwise. 130 * @return {boolean} True if resize-to-client is enabled; false otherwise.
153 */ 131 */
154 remoting.DesktopConnectedView.prototype.getResizeToClient = function() { 132 remoting.DesktopConnectedView.prototype.getResizeToClient = function() {
155 return this.host_.options.resizeToClient; 133 if (this.viewport_) {
134 return this.viewport_.getResizeToClient();
135 }
136 return false;
156 }; 137 };
157 138
158 /** 139 /**
159 * @return {Element} The element that should host the plugin. 140 * @return {Element} The element that should host the plugin.
141 * @private
160 */ 142 */
161 remoting.DesktopConnectedView.prototype.getPluginContainer = function() { 143 remoting.DesktopConnectedView.prototype.getPluginContainer_ = function() {
162 return this.container_.querySelector('.client-plugin-container') 144 return this.container_.querySelector('.client-plugin-container');
145 };
146
147 /** @return {remoting.DesktopViewport} */
148 remoting.DesktopConnectedView.prototype.getViewportForTesting = function() {
149 return this.viewport_;
163 }; 150 };
164 151
165 /** 152 /**
166 * @return {{width: number, height: number}} The height of the window's client
167 * area. This differs between apps v1 and apps v2 due to the custom window
168 * borders used by the latter.
169 * TODO: make private
170 */
171 remoting.DesktopConnectedView.prototype.getClientArea_ = function() {
172 return remoting.windowFrame ?
173 remoting.windowFrame.getClientArea() :
174 { 'width': window.innerWidth, 'height': window.innerHeight };
175 };
176
177 /**
178 * @param {number} width
179 * @param {number} height
180 */
181 remoting.DesktopConnectedView.prototype.setPluginSizeForBumpScrollTesting =
182 function(width, height) {
183 this.pluginWidthForBumpScrollTesting_ = width;
184 this.pluginHeightForBumpScrollTesting_ = height;
185 }
186
187 /**
188 * Notifies the host of the client's current dimensions and DPI.
189 * Also takes into account per-host scaling factor, if configured.
190 * TODO: private
191 */
192 remoting.DesktopConnectedView.prototype.notifyClientResolution_ = function() {
193 var clientArea = this.getClientArea_();
194 var desktopScale = this.host_.options.desktopScale;
195 this.plugin_.hostDesktop().resize(clientArea.width * desktopScale,
196 clientArea.height * desktopScale,
197 window.devicePixelRatio);
198 };
199
200 /**
201 * Adds <embed> element to the UI container and readies the session object. 153 * Adds <embed> element to the UI container and readies the session object.
202 * 154 *
203 * @param {function(string, string):boolean} onExtensionMessage The handler for 155 * @param {function(string, string):boolean} onExtensionMessage The handler for
204 * protocol extension messages. Returns true if a message is recognized; 156 * protocol extension messages. Returns true if a message is recognized;
205 * false otherwise. 157 * false otherwise.
206 * @param {Array<string>} requiredCapabilities A list of capabilities 158 * @param {Array<string>} requiredCapabilities A list of capabilities
207 * required by this application. 159 * required by this application.
208 */ 160 */
209 remoting.DesktopConnectedView.prototype.createPluginAndConnect = 161 remoting.DesktopConnectedView.prototype.createPluginAndConnect =
210 function(onExtensionMessage, requiredCapabilities) { 162 function(onExtensionMessage, requiredCapabilities) {
211 this.plugin_ = remoting.ClientPlugin.factory.createPlugin( 163 this.plugin_ = remoting.ClientPlugin.factory.createPlugin(
212 this.getPluginContainer(), 164 this.getPluginContainer_(),
213 onExtensionMessage, requiredCapabilities); 165 onExtensionMessage, requiredCapabilities);
214 var that = this; 166 var that = this;
215 this.host_.options.load().then(function(){ 167 this.host_.options.load().then(function(){
216 that.plugin_.initialize(that.onPluginInitialized_.bind(that)); 168 that.plugin_.initialize(that.onPluginInitialized_.bind(that));
217 }); 169 });
218 }; 170 };
219 171
220 /** 172 /**
221 * @param {boolean} initialized 173 * @param {boolean} initialized
222 */ 174 */
(...skipping 27 matching lines...) Expand all
250 this.applyRemapKeys_(true); 202 this.applyRemapKeys_(true);
251 } 203 }
252 204
253 // TODO(wez): Only allow mouse lock if the app has the pointerLock permission. 205 // TODO(wez): Only allow mouse lock if the app has the pointerLock permission.
254 // Enable automatic mouse-lock. 206 // Enable automatic mouse-lock.
255 if (remoting.enableMouseLock && 207 if (remoting.enableMouseLock &&
256 this.plugin_.hasFeature(remoting.ClientPlugin.Feature.ALLOW_MOUSE_LOCK)) { 208 this.plugin_.hasFeature(remoting.ClientPlugin.Feature.ALLOW_MOUSE_LOCK)) {
257 this.plugin_.allowMouseLock(); 209 this.plugin_.allowMouseLock();
258 } 210 }
259 211
260 base.dispose(this.eventHooks_);
261 var hostDesktop = this.plugin_.hostDesktop();
262 this.eventHooks_ = new base.Disposables(
263 new base.EventHook(
264 hostDesktop, remoting.HostDesktop.Events.sizeChanged,
265 this.onDesktopSizeChanged_.bind(this)),
266 new base.EventHook(
267 hostDesktop, remoting.HostDesktop.Events.shapeChanged,
268 this.onDesktopShapeChanged_.bind(this)));
269
270 this.plugin_.setMouseCursorHandler(this.updateMouseCursorImage_.bind(this)); 212 this.plugin_.setMouseCursorHandler(this.updateMouseCursorImage_.bind(this));
271 213
272 this.onInitialized_(remoting.Error.NONE, this.plugin_); 214 this.onInitialized_(remoting.Error.NONE, this.plugin_);
273 }; 215 };
274 216
275 /** 217 /**
276 * This is a callback that gets called when the plugin notifies us of a change
277 * in the size of the remote desktop.
278 *
279 * @return {void} Nothing.
280 * @private
281 */
282 remoting.DesktopConnectedView.prototype.onDesktopSizeChanged_ = function() {
283 var desktop = this.plugin_.hostDesktop().getDimensions();
284 console.log('desktop size changed: ' +
285 desktop.width + 'x' +
286 desktop.height +' @ ' +
287 desktop.xDpi + 'x' +
288 desktop.yDpi + ' DPI');
289 this.updateDimensions();
290 this.updateScrollbarVisibility();
291 };
292
293 /**
294 * Sets the non-click-through area of the client in response to notifications
295 * from the plugin of desktop shape changes.
296 *
297 * @param {Array<Array<number>>} rects List of rectangles comprising the
298 * desktop shape.
299 * @return {void} Nothing.
300 * @private
301 */
302 remoting.DesktopConnectedView.prototype.onDesktopShapeChanged_ = function(
303 rects) {
304 // Build the list of rects for the input region.
305 var inputRegion = [];
306 for (var i = 0; i < rects.length; ++i) {
307 var rect = {};
308 rect.left = rects[i][0];
309 rect.top = rects[i][1];
310 rect.width = rects[i][2];
311 rect.height = rects[i][3];
312 inputRegion.push(rect);
313 }
314
315 remoting.windowShape.setDesktopRects(inputRegion);
316 };
317
318 /**
319 * This is a callback that gets called when the window is resized. 218 * This is a callback that gets called when the window is resized.
320 * 219 *
321 * @return {void} Nothing. 220 * @return {void} Nothing.
322 */ 221 */
323 remoting.DesktopConnectedView.prototype.onResize = function() { 222 remoting.DesktopConnectedView.prototype.onResize = function() {
324 this.updateDimensions(); 223 if (this.viewport_) {
325 224 this.viewport_.onResize();
326 if (this.notifyClientResolutionTimer_) {
327 window.clearTimeout(this.notifyClientResolutionTimer_);
328 this.notifyClientResolutionTimer_ = null;
329 } 225 }
330
331 // Defer notifying the host of the change until the window stops resizing, to
332 // avoid overloading the control channel with notifications.
333 if (this.getResizeToClient()) {
334 var kResizeRateLimitMs = 250;
335 var clientArea = this.getClientArea_();
336 this.notifyClientResolutionTimer_ = window.setTimeout(
337 this.notifyClientResolution_.bind(this),
338 kResizeRateLimitMs);
339 }
340
341 // If bump-scrolling is enabled, adjust the plugin margins to fully utilize
342 // the new window area.
343 this.resetScroll_();
344
345 this.updateScrollbarVisibility();
346 }; 226 };
347 227
348 /** 228 /**
349 * Callback that the plugin invokes to indicate when the connection is 229 * Callback that the plugin invokes to indicate when the connection is
350 * ready. 230 * ready.
351 * 231 *
352 * @param {boolean} ready True if the connection is ready. 232 * @param {boolean} ready True if the connection is ready.
353 */ 233 */
354 remoting.DesktopConnectedView.prototype.onConnectionReady = function(ready) { 234 remoting.DesktopConnectedView.prototype.onConnectionReady = function(ready) {
355 if (!ready) { 235 if (!ready) {
356 this.container_.classList.add('session-client-inactive'); 236 this.container_.classList.add('session-client-inactive');
357 } else { 237 } else {
358 this.container_.classList.remove('session-client-inactive'); 238 this.container_.classList.remove('session-client-inactive');
359 } 239 }
360 }; 240 };
361 241
362 /** 242 /**
363 * Deletes the <embed> element from the container, without sending a 243 * Deletes the <embed> element from the container, without sending a
364 * session_terminate request. This is to be called when the session was 244 * session_terminate request. This is to be called when the session was
365 * disconnected by the Host. 245 * disconnected by the Host.
366 * 246 *
367 * @return {void} Nothing. 247 * @return {void} Nothing.
368 */ 248 */
369 remoting.DesktopConnectedView.prototype.removePlugin = function() { 249 remoting.DesktopConnectedView.prototype.removePlugin = function() {
370 if (this.plugin_) { 250 if (this.plugin_) {
371 base.dispose(this.eventHooks_);
372 this.eventHooks_ = null;
373 this.plugin_.element().removeEventListener( 251 this.plugin_.element().removeEventListener(
374 'focus', this.callPluginGotFocus_, false); 252 'focus', this.callPluginGotFocus_, false);
375 this.plugin_.element().removeEventListener( 253 this.plugin_.element().removeEventListener(
376 'blur', this.callPluginLostFocus_, false); 254 'blur', this.callPluginLostFocus_, false);
377 this.plugin_.dispose(); 255 this.plugin_.dispose();
378 this.plugin_ = null; 256 this.plugin_ = null;
379 } 257 }
380 258
381 this.updateClientSessionUi_(null); 259 this.updateClientSessionUi_(null);
382 }; 260 };
(...skipping 14 matching lines...) Expand all
397 if (remoting.optionsMenu) { 275 if (remoting.optionsMenu) {
398 remoting.optionsMenu.setDesktopConnectedView(null); 276 remoting.optionsMenu.setDesktopConnectedView(null);
399 } 277 }
400 278
401 document.body.classList.remove('connected'); 279 document.body.classList.remove('connected');
402 this.container_.removeEventListener( 280 this.container_.removeEventListener(
403 'mousemove', this.updateMouseCursorPosition_, true); 281 'mousemove', this.updateMouseCursorPosition_, true);
404 // Stop listening for full-screen events. 282 // Stop listening for full-screen events.
405 remoting.fullscreen.removeListener(this.callOnFullScreenChanged_); 283 remoting.fullscreen.removeListener(this.callOnFullScreenChanged_);
406 284
285 base.dispose(this.viewport_);
286 this.viewport_ = null;
407 } else { 287 } else {
288 var scrollerElement = document.getElementById('scroller');
289 this.viewport_ = new remoting.DesktopViewport(
290 scrollerElement || document.body,
291 this.plugin_.hostDesktop(),
292 this.host_.options);
408 if (remoting.windowFrame) { 293 if (remoting.windowFrame) {
409 remoting.windowFrame.setDesktopConnectedView(this); 294 remoting.windowFrame.setDesktopConnectedView(this);
410 } 295 }
411 if (remoting.toolbar) { 296 if (remoting.toolbar) {
412 remoting.toolbar.setDesktopConnectedView(this); 297 remoting.toolbar.setDesktopConnectedView(this);
413 } 298 }
414 if (remoting.optionsMenu) { 299 if (remoting.optionsMenu) {
415 remoting.optionsMenu.setDesktopConnectedView(this); 300 remoting.optionsMenu.setDesktopConnectedView(this);
416 } 301 }
417 302
418 if (this.getResizeToClient()) {
419 this.notifyClientResolution_();
420 }
421
422 document.body.classList.add('connected'); 303 document.body.classList.add('connected');
423 this.container_.addEventListener( 304 this.container_.addEventListener(
424 'mousemove', this.updateMouseCursorPosition_, true); 305 'mousemove', this.updateMouseCursorPosition_, true);
425 // Activate full-screen related UX. 306 // Activate full-screen related UX.
426 remoting.fullscreen.addListener(this.callOnFullScreenChanged_); 307 remoting.fullscreen.addListener(this.callOnFullScreenChanged_);
427 this.onDesktopSizeChanged_();
428 this.setFocusHandlers_(); 308 this.setFocusHandlers_();
429 } 309 }
430 }; 310 };
431 311
432 /** 312 /**
433 * @return {{top: number, left:number}} The top-left corner of the plugin.
434 */
435 remoting.DesktopConnectedView.prototype.getPluginPositionForTesting = function(
436 ) {
437 var style = this.container_.style;
438 return {
439 top: parseFloat(style.marginTop),
440 left: parseFloat(style.marginLeft)
441 };
442 };
443
444 /**
445 * Constrains the focus to the plugin element. 313 * Constrains the focus to the plugin element.
446 * @private 314 * @private
447 */ 315 */
448 remoting.DesktopConnectedView.prototype.setFocusHandlers_ = function() { 316 remoting.DesktopConnectedView.prototype.setFocusHandlers_ = function() {
449 this.plugin_.element().addEventListener( 317 this.plugin_.element().addEventListener(
450 'focus', this.callPluginGotFocus_, false); 318 'focus', this.callPluginGotFocus_, false);
451 this.plugin_.element().addEventListener( 319 this.plugin_.element().addEventListener(
452 'blur', this.callPluginLostFocus_, false); 320 'blur', this.callPluginLostFocus_, false);
453 this.plugin_.element().focus(); 321 this.plugin_.element().focus();
454 }; 322 };
455 323
456 /** 324 /**
457 * Set the shrink-to-fit and resize-to-client flags and save them if this is 325 * Set the shrink-to-fit and resize-to-client flags and save them if this is
458 * a Me2Me connection. 326 * a Me2Me connection.
459 * 327 *
460 * @param {boolean} shrinkToFit True if the remote desktop should be scaled 328 * @param {boolean} shrinkToFit True if the remote desktop should be scaled
461 * down if it is larger than the client window; false if scroll-bars 329 * down if it is larger than the client window; false if scroll-bars
462 * should be added in this case. 330 * should be added in this case.
463 * @param {boolean} resizeToClient True if window resizes should cause the 331 * @param {boolean} resizeToClient True if window resizes should cause the
464 * host to attempt to resize its desktop to match the client window size; 332 * host to attempt to resize its desktop to match the client window size;
465 * false to disable this behaviour for subsequent window resizes--the 333 * false to disable this behaviour for subsequent window resizes--the
466 * current host desktop size is not restored in this case. 334 * current host desktop size is not restored in this case.
467 * @return {void} Nothing. 335 * @return {void} Nothing.
468 */ 336 */
469 remoting.DesktopConnectedView.prototype.setScreenMode = 337 remoting.DesktopConnectedView.prototype.setScreenMode =
470 function(shrinkToFit, resizeToClient) { 338 function(shrinkToFit, resizeToClient) {
471 if (resizeToClient && !this.getResizeToClient()) { 339 this.viewport_.setScreenMode(shrinkToFit, resizeToClient);
472 this.notifyClientResolution_();
473 }
474
475 // If enabling shrink, reset bump-scroll offsets.
476 var needsScrollReset = shrinkToFit && !this.getShrinkToFit();
477
478 this.host_.options.shrinkToFit = shrinkToFit;
479 this.host_.options.resizeToClient = resizeToClient;
480 this.updateScrollbarVisibility();
481 this.host_.options.save();
482
483 this.updateDimensions();
484 if (needsScrollReset) {
485 this.resetScroll_();
486 }
487 }; 340 };
488 341
489 /** 342 /**
490 * Sets and stores the scale factor to apply to host sizing requests.
491 * The desktopScale applies to the dimensions reported to the host, not
492 * to the client DPI reported to it.
493 *
494 * @param {number} desktopScale Scale factor to apply.
495 */
496 remoting.DesktopConnectedView.prototype.setDesktopScale = function(
497 desktopScale) {
498 this.host_.options.desktopScale = desktopScale;
499
500 // onResize() will update the plugin size and scrollbars for the new
501 // scaled plugin dimensions, and send a client resolution notification.
502 this.onResize();
503
504 // Save the new desktop scale setting.
505 this.host_.options.save();
506 };
507
508 /**
509 * Called when the full-screen status has changed, either via the 343 * Called when the full-screen status has changed, either via the
510 * remoting.Fullscreen class, or via a system event such as the Escape key 344 * remoting.Fullscreen class, or via a system event such as the Escape key
511 * 345 *
512 * @param {boolean=} fullscreen True if the app is entering full-screen mode; 346 * @param {boolean=} fullscreen True if the app is entering full-screen mode;
513 * false if it is leaving it. 347 * false if it is leaving it.
514 * @private 348 * @private
515 */ 349 */
516 remoting.DesktopConnectedView.prototype.onFullScreenChanged_ = function ( 350 remoting.DesktopConnectedView.prototype.onFullScreenChanged_ = function (
517 fullscreen) { 351 fullscreen) {
518 this.enableBumpScroll_(fullscreen); 352 if (this.viewport_) {
353 this.viewport_.enableBumpScroll(Boolean(fullscreen));
354 }
519 }; 355 };
520 356
521 /** 357 /**
522 * Callback function called when the plugin element gets focus. 358 * Callback function called when the plugin element gets focus.
523 */ 359 */
524 remoting.DesktopConnectedView.prototype.pluginGotFocus_ = function() { 360 remoting.DesktopConnectedView.prototype.pluginGotFocus_ = function() {
525 remoting.clipboard.initiateToHost(); 361 remoting.clipboard.initiateToHost();
526 }; 362 };
527 363
528 /** 364 /**
(...skipping 22 matching lines...) Expand all
551 function(url, hotspotX, hotspotY) { 387 function(url, hotspotX, hotspotY) {
552 this.mouseCursorOverlay_.hidden = !url; 388 this.mouseCursorOverlay_.hidden = !url;
553 if (url) { 389 if (url) {
554 this.mouseCursorOverlay_.style.marginLeft = '-' + hotspotX + 'px'; 390 this.mouseCursorOverlay_.style.marginLeft = '-' + hotspotX + 'px';
555 this.mouseCursorOverlay_.style.marginTop = '-' + hotspotY + 'px'; 391 this.mouseCursorOverlay_.style.marginTop = '-' + hotspotY + 'px';
556 this.mouseCursorOverlay_.src = url; 392 this.mouseCursorOverlay_.src = url;
557 } 393 }
558 }; 394 };
559 395
560 /** 396 /**
561 * Enable or disable bump-scrolling. When disabling bump scrolling, also reset
562 * the scroll offsets to (0, 0).
563 * @param {boolean=} enable True to enable bump-scrolling, false to disable it.
564 * @private
565 */
566 remoting.DesktopConnectedView.prototype.enableBumpScroll_ = function(enable) {
567 var element = /** @type{HTMLElement} */ (document.documentElement);
568 if (enable) {
569 /** @type {null|function(Event):void} */
570 this.onMouseMoveRef_ = this.onMouseMove_.bind(this);
571 element.addEventListener('mousemove', this.onMouseMoveRef_, false);
572 } else {
573 element.removeEventListener('mousemove', this.onMouseMoveRef_, false);
574 this.onMouseMoveRef_ = null;
575 this.resetScroll_();
576 }
577 };
578
579 remoting.DesktopConnectedView.prototype.resetScroll_ = function() {
580 this.container_.style.marginTop = '0px';
581 this.container_.style.marginLeft = '0px';
582 };
583
584 /**
585 * @param {Event} event The mouse event.
586 * @private
587 */
588 remoting.DesktopConnectedView.prototype.onMouseMove_ = function(event) {
589 if (this.bumpScrollTimer_) {
590 window.clearTimeout(this.bumpScrollTimer_);
591 this.bumpScrollTimer_ = null;
592 }
593
594 /**
595 * Compute the scroll speed based on how close the mouse is to the edge.
596 * @param {number} mousePos The mouse x- or y-coordinate
597 * @param {number} size The width or height of the content area.
598 * @return {number} The scroll delta, in pixels.
599 */
600 var computeDelta = function(mousePos, size) {
601 var threshold = 10;
602 if (mousePos >= size - threshold) {
603 return 1 + 5 * (mousePos - (size - threshold)) / threshold;
604 } else if (mousePos <= threshold) {
605 return -1 - 5 * (threshold - mousePos) / threshold;
606 }
607 return 0;
608 };
609
610 var clientArea = this.getClientArea_();
611 var dx = computeDelta(event.x, clientArea.width);
612 var dy = computeDelta(event.y, clientArea.height);
613
614 if (dx != 0 || dy != 0) {
615 this.raiseEvent(remoting.DesktopConnectedView.Events.bumpScrollStarted);
616 /** @type {remoting.DesktopConnectedView} */
617 var that = this;
618 /**
619 * Scroll the view, and schedule a timer to do so again unless we've hit
620 * the edges of the screen. This timer is cancelled when the mouse moves.
621 * @param {number} expected The time at which we expect to be called.
622 */
623 var repeatScroll = function(expected) {
624 /** @type {number} */
625 var now = new Date().getTime();
626 /** @type {number} */
627 var timeout = 10;
628 var lateAdjustment = 1 + (now - expected) / timeout;
629 if (that.scroll_(lateAdjustment * dx, lateAdjustment * dy)) {
630 that.raiseEvent(remoting.DesktopConnectedView.Events.bumpScrollStopped);
631 } else {
632 that.bumpScrollTimer_ = window.setTimeout(
633 function() { repeatScroll(now + timeout); },
634 timeout);
635 }
636 };
637 repeatScroll(new Date().getTime());
638 }
639 };
640
641 /**
642 * Scroll the client plugin by the specified amount, keeping it visible.
643 * Note that this is only used in content full-screen mode (not windowed or
644 * browser full-screen modes), where window.scrollBy and the scrollTop and
645 * scrollLeft properties don't work.
646 * @param {number} dx The amount by which to scroll horizontally. Positive to
647 * scroll right; negative to scroll left.
648 * @param {number} dy The amount by which to scroll vertically. Positive to
649 * scroll down; negative to scroll up.
650 * @return {boolean} True if the requested scroll had no effect because both
651 * vertical and horizontal edges of the screen have been reached.
652 * @private
653 */
654 remoting.DesktopConnectedView.prototype.scroll_ = function(dx, dy) {
655 /**
656 * Helper function for x- and y-scrolling
657 * @param {number|string} curr The current margin, eg. "10px".
658 * @param {number} delta The requested scroll amount.
659 * @param {number} windowBound The size of the window, in pixels.
660 * @param {number} pluginBound The size of the plugin, in pixels.
661 * @param {{stop: boolean}} stop Reference parameter used to indicate when
662 * the scroll has reached one of the edges and can be stopped in that
663 * direction.
664 * @return {string} The new margin value.
665 */
666 var adjustMargin = function(curr, delta, windowBound, pluginBound, stop) {
667 var minMargin = Math.min(0, windowBound - pluginBound);
668 var result = (curr ? parseFloat(curr) : 0) - delta;
669 result = Math.min(0, Math.max(minMargin, result));
670 stop.stop = (result == 0 || result == minMargin);
671 return result + 'px';
672 };
673
674 var plugin = this.plugin_.element();
675 var style = this.container_.style;
676
677 var stopX = { stop: false };
678 var clientArea = this.getClientArea_();
679 style.marginLeft = adjustMargin(style.marginLeft, dx, clientArea.width,
680 this.pluginWidthForBumpScrollTesting_ || plugin.clientWidth, stopX);
681
682 var stopY = { stop: false };
683 style.marginTop = adjustMargin(
684 style.marginTop, dy, clientArea.height,
685 this.pluginHeightForBumpScrollTesting_ || plugin.clientHeight, stopY);
686 return stopX.stop && stopY.stop;
687 };
688
689 /**
690 * Refreshes the plugin's dimensions, taking into account the sizes of the
691 * remote desktop and client window, and the current scale-to-fit setting.
692 *
693 * @return {void} Nothing.
694 */
695 remoting.DesktopConnectedView.prototype.updateDimensions = function() {
696 var desktopSize = this.plugin_.hostDesktop().getDimensions();
697
698 if (desktopSize.width === 0 ||
699 desktopSize.height === 0) {
700 return;
701 }
702
703 var desktopDpi = { x: desktopSize.xDpi,
704 y: desktopSize.yDpi };
705 var newSize = remoting.DesktopConnectedView.choosePluginSize(
706 this.getClientArea_(), window.devicePixelRatio,
707 desktopSize, desktopDpi, this.host_.options.desktopScale,
708 remoting.fullscreen.isActive(), this.getShrinkToFit());
709
710 // Resize the plugin if necessary.
711 console.log('plugin dimensions:' + newSize.width + 'x' + newSize.height);
712 this.plugin_.element().style.width = newSize.width + 'px';
713 this.plugin_.element().style.height = newSize.height + 'px';
714
715 // When we receive the first plugin dimensions from the host, we know that
716 // remote host has started.
717 remoting.app.onVideoStreamingStarted();
718 }
719
720 /**
721 * Helper function accepting client and host dimensions, and returning a chosen
722 * size for the plugin element, in DIPs.
723 *
724 * @param {{width: number, height: number}} clientSizeDips Available client
725 * dimensions, in DIPs.
726 * @param {number} clientPixelRatio Number of physical pixels per client DIP.
727 * @param {{width: number, height: number}} desktopSize Size of the host desktop
728 * in physical pixels.
729 * @param {{x: number, y: number}} desktopDpi DPI of the host desktop in both
730 * dimensions.
731 * @param {number} desktopScale The scale factor configured for the host.
732 * @param {boolean} isFullscreen True if full-screen mode is active.
733 * @param {boolean} shrinkToFit True if shrink-to-fit should be applied.
734 * @return {{width: number, height: number}} Chosen plugin dimensions, in DIPs.
735 */
736 remoting.DesktopConnectedView.choosePluginSize = function(
737 clientSizeDips, clientPixelRatio, desktopSize, desktopDpi, desktopScale,
738 isFullscreen, shrinkToFit) {
739 base.debug.assert(clientSizeDips.width > 0);
740 base.debug.assert(clientSizeDips.height > 0);
741 base.debug.assert(clientPixelRatio >= 1.0);
742 base.debug.assert(desktopSize.width > 0);
743 base.debug.assert(desktopSize.height > 0);
744 base.debug.assert(desktopDpi.x > 0);
745 base.debug.assert(desktopDpi.y > 0);
746 base.debug.assert(desktopScale > 0);
747
748 // We have the following goals in sizing the desktop display at the client:
749 // 1. Avoid losing detail by down-scaling beyond 1:1 host:device pixels.
750 // 2. Avoid up-scaling if that will cause the client to need scrollbars.
751 // 3. Avoid introducing blurriness with non-integer up-scaling factors.
752 // 4. Avoid having huge "letterboxes" around the desktop, if it's really
753 // small.
754 // 5. Compensate for mismatched DPIs, so that the behaviour of features like
755 // shrink-to-fit matches their "natural" rather than their pixel size.
756 // e.g. with shrink-to-fit active a 1024x768 low-DPI host on a 640x480
757 // high-DPI client will be up-scaled to 1280x960, rather than displayed
758 // at 1:1 host:physical client pixels.
759 //
760 // To determine the ideal size we follow a four-stage process:
761 // 1. Determine the "natural" size at which to display the desktop.
762 // a. Initially assume 1:1 mapping of desktop to client device pixels.
763 // b. If host DPI is less than the client's then up-scale accordingly.
764 // c. If desktopScale is configured for the host then allow that to
765 // reduce the amount of up-scaling from (b). e.g. if the client:host
766 // DPIs are 2:1 then a desktopScale of 1.5 would reduce the up-scale
767 // to 4:3, while a desktopScale of 3.0 would result in no up-scaling.
768 // 2. If the natural size of the desktop is smaller than the client device
769 // then apply up-scaling by an integer scale factor to avoid excessive
770 // letterboxing.
771 // 3. If shrink-to-fit is configured then:
772 // a. If the natural size exceeds the client size then apply down-scaling
773 // by an arbitrary scale factor.
774 // b. If we're in full-screen mode and the client & host aspect-ratios
775 // are radically different (e.g. the host is actually multi-monitor)
776 // then shrink-to-fit to the shorter dimension, rather than leaving
777 // huge letterboxes; the user can then bump-scroll around the desktop.
778 // 4. If the overall scale factor is fractionally over an integer factor
779 // then reduce it to that integer factor, to avoid blurring.
780
781 // All calculations are performed in device pixels.
782 var clientWidth = clientSizeDips.width * clientPixelRatio;
783 var clientHeight = clientSizeDips.height * clientPixelRatio;
784
785 // 1. Determine a "natural" size at which to display the desktop.
786 var scale = 1.0;
787
788 // Determine the effective host device pixel ratio.
789 // Note that we round up or down to the closest integer pixel ratio.
790 var hostPixelRatioX = Math.round(desktopDpi.x / 96);
791 var hostPixelRatioY = Math.round(desktopDpi.y / 96);
792 var hostPixelRatio = Math.min(hostPixelRatioX, hostPixelRatioY);
793
794 // Allow up-scaling to account for DPI.
795 scale = Math.max(scale, clientPixelRatio / hostPixelRatio);
796
797 // Allow some or all of the up-scaling to be cancelled by the desktopScale.
798 if (desktopScale > 1.0) {
799 scale = Math.max(1.0, scale / desktopScale);
800 }
801
802 // 2. If the host is still much smaller than the client, then up-scale to
803 // avoid wasting space, but only by an integer factor, to avoid blurring.
804 if (desktopSize.width * scale <= clientWidth &&
805 desktopSize.height * scale <= clientHeight) {
806 var scaleX = Math.floor(clientWidth / desktopSize.width);
807 var scaleY = Math.floor(clientHeight / desktopSize.height);
808 scale = Math.min(scaleX, scaleY);
809 base.debug.assert(scale >= 1.0);
810 }
811
812 // 3. Apply shrink-to-fit, if configured.
813 if (shrinkToFit) {
814 var scaleFitWidth = Math.min(scale, clientWidth / desktopSize.width);
815 var scaleFitHeight = Math.min(scale, clientHeight / desktopSize.height);
816 scale = Math.min(scaleFitHeight, scaleFitWidth);
817
818 // If we're running full-screen then try to handle common side-by-side
819 // multi-monitor combinations more intelligently.
820 if (isFullscreen) {
821 // If the host has two monitors each the same size as the client then
822 // scale-to-fit will have the desktop occupy only 50% of the client area,
823 // in which case it would be preferable to down-scale less and let the
824 // user bump-scroll around ("scale-and-pan").
825 // Triggering scale-and-pan if less than 65% of the client area would be
826 // used adds enough fuzz to cope with e.g. 1280x800 client connecting to
827 // a (2x1280)x1024 host nicely.
828 // Note that we don't need to account for scrollbars while fullscreen.
829 if (scale <= scaleFitHeight * 0.65) {
830 scale = scaleFitHeight;
831 }
832 if (scale <= scaleFitWidth * 0.65) {
833 scale = scaleFitWidth;
834 }
835 }
836 }
837
838 // 4. Avoid blurring for close-to-integer up-scaling factors.
839 if (scale > 1.0) {
840 var scaleBlurriness = scale / Math.floor(scale);
841 if (scaleBlurriness < 1.1) {
842 scale = Math.floor(scale);
843 }
844 }
845
846 // Return the necessary plugin dimensions in DIPs.
847 scale = scale / clientPixelRatio;
848 var pluginWidth = Math.round(desktopSize.width * scale);
849 var pluginHeight = Math.round(desktopSize.height * scale);
850 return { width: pluginWidth, height: pluginHeight };
851 }
852
853 /**
854 * Called when the window or desktop size or the scaling settings change,
855 * to set the scroll-bar visibility.
856 *
857 * TODO(jamiewalch): crbug.com/252796: Remove this once crbug.com/240772 is
858 * fixed.
859 */
860 remoting.DesktopConnectedView.prototype.updateScrollbarVisibility = function() {
861 var scroller = document.getElementById('scroller');
862 if (!scroller) {
863 return;
864 }
865
866 var needsVerticalScroll = false;
867 var needsHorizontalScroll = false;
868 if (!this.getShrinkToFit()) {
869 // Determine whether or not horizontal or vertical scrollbars are
870 // required, taking into account their width.
871 var clientArea = this.getClientArea_();
872 var desktopSize = this.plugin_.hostDesktop().getDimensions();
873 needsVerticalScroll = clientArea.height < desktopSize.height;
874 needsHorizontalScroll = clientArea.width < desktopSize.width;
875 var kScrollBarWidth = 16;
876 if (needsHorizontalScroll && !needsVerticalScroll) {
877 needsVerticalScroll =
878 clientArea.height - kScrollBarWidth < desktopSize.height;
879 } else if (!needsHorizontalScroll && needsVerticalScroll) {
880 needsHorizontalScroll =
881 clientArea.width - kScrollBarWidth < desktopSize.width;
882 }
883 }
884
885 if (needsHorizontalScroll) {
886 scroller.classList.remove('no-horizontal-scroll');
887 } else {
888 scroller.classList.add('no-horizontal-scroll');
889 }
890 if (needsVerticalScroll) {
891 scroller.classList.remove('no-vertical-scroll');
892 } else {
893 scroller.classList.add('no-vertical-scroll');
894 }
895 };
896
897 /**
898 * Sets and stores the key remapping setting for the current host. 397 * Sets and stores the key remapping setting for the current host.
899 * 398 *
900 * @param {string} remappings Comma separated list of key remappings. 399 * @param {string} remappings Comma separated list of key remappings.
901 */ 400 */
902 remoting.DesktopConnectedView.prototype.setRemapKeys = function(remappings) { 401 remoting.DesktopConnectedView.prototype.setRemapKeys = function(remappings) {
903 // Cancel any existing remappings and apply the new ones. 402 // Cancel any existing remappings and apply the new ones.
904 this.applyRemapKeys_(false); 403 this.applyRemapKeys_(false);
905 this.host_.options.remapKeys = remappings; 404 this.host_.options.remapKeys = remappings;
906 this.applyRemapKeys_(true); 405 this.applyRemapKeys_(true);
907 406
(...skipping 138 matching lines...) Expand 10 before | Expand all | Expand 10 after
1046 * @param {Object} message The parsed extension message data. 545 * @param {Object} message The parsed extension message data.
1047 * @return {boolean} True if the message was recognized, false otherwise. 546 * @return {boolean} True if the message was recognized, false otherwise.
1048 */ 547 */
1049 remoting.DesktopConnectedView.prototype.handleExtensionMessage = 548 remoting.DesktopConnectedView.prototype.handleExtensionMessage =
1050 function(type, message) { 549 function(type, message) {
1051 if (this.videoFrameRecorder_) { 550 if (this.videoFrameRecorder_) {
1052 return this.videoFrameRecorder_.handleMessage(type, message); 551 return this.videoFrameRecorder_.handleMessage(type, message);
1053 } 552 }
1054 return false; 553 return false;
1055 }; 554 };
OLDNEW
« no previous file with comments | « remoting/webapp/crd/js/client_session.js ('k') | remoting/webapp/crd/js/desktop_viewport.js » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698