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

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

Issue 918783002: CRD Viewport Management refactor (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: Implementation 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
OLDNEW
(Empty)
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
3 // found in the LICENSE file.
4
5 /**
6 * @fileoverview
7 * Provides view port management utilities below for a desktop remoting session.
8 * - Enabling bump scrolling
9 * - Resizing the viewport to fit the host desktop
10 * - Resizing the host desktop to fit the client viewport.
11 */
12
13 /** @suppress {duplicate} */
14 var remoting = remoting || {};
15
16 (function() {
17
18 'use strict';
19
20 /**
21 * @param {HTMLElement} rootElement (The scroller element)
garykac 2015/02/12 18:18:22 scroller element?
kelvinp 2015/02/12 23:26:41 Done.
22 * @param {remoting.ClientPlugin.HostDesktop} hostDesktop
23 * @param {string} hostId ID of the host.
24 *
25 * TODO(kelvinp): Remove hostId from the constructor params once we can
26 * better encapsulate the host setting interface.
27 * @constructor
28 * @implements {base.Disposable}
29 */
30 remoting.DesktopViewport = function(rootElement, hostDesktop, hostId) {
31 /** @private */
32 this.rootElement_ = rootElement;
33 /** @private */
34 // TODO(kelvinp): Query the container by class name instead of id.
35 this.pluginContainer_ = rootElement.querySelector('#client-container');
36 /** @private */
37 this.pluginElement_ = rootElement.querySelector('embed');
38 /** @private */
39 this.hostDesktop_ = hostDesktop;
40 /** @private */
41 this.shrinkToFit_ = true;
42 /** @private */
43 this.resizeToClient_ = true;
44 /** @private */
45 this.desktopScale_ = 1.0;
46 /** @private */
47 this.hostId_ = hostId;
48 /** @private {number?} */
49 this.resizeTimer_ = null;
50 /** @private {remoting.BumpScroller} */
51 this.bumpScroller_ = null;
52 // Bump-scroll test variables. Override to use a fake value for the width
53 // and height of the client plugin so that bump-scrolling can be tested
54 // without relying on the actual size of the host desktop.
55 /** @private {number} */
56 this.pluginWidthForBumpScrollTesting = 0;
57 /** @private {number} */
58 this.pluginHeightForBumpScrollTesting = 0;
59
60 this.eventHook_ = new base.EventHook(
61 this.hostDesktop_, remoting.ClientPlugin.HostDesktop.Events.sizeChanged,
62 this.onDesktopSizeChanged_.bind(this));
63 };
64
65 remoting.DesktopViewport.prototype.dispose = function() {
66 base.dispose(this.eventHook_);
67 this.eventHook_ = null;
68 };
69
70 /**
71 * @return {boolean} True if shrink-to-fit is enabled; false otherwise.
72 */
73 remoting.DesktopViewport.prototype.getShrinkToFit = function() {
74 return this.shrinkToFit_;
75 };
76
77 /**
78 * @return {boolean} True if resize-to-client is enabled; false otherwise.
79 */
80 remoting.DesktopViewport.prototype.getResizeToClient = function() {
81 return this.resizeToClient_;
82 };
83
84 /**
85 * Set the shrink-to-fit and resize-to-client flags and save them if this is
86 * a Me2Me connection.
87 *
88 * @param {boolean} shrinkToFit True if the remote desktop should be scaled
89 * down if it is larger than the client window; false if scroll-bars
90 * should be added in this case.
91 * @param {boolean} resizeToClient True if window resizes should cause the
92 * host to attempt to resize its desktop to match the client window size;
93 * false to disable this behaviour for subsequent window resizes--the
94 * current host desktop size is not restored in this case.
95 * @return {void} Nothing.
96 */
97 remoting.DesktopViewport.prototype.setScreenMode =
98 function(shrinkToFit, resizeToClient) {
99 if (resizeToClient && !this.resizeToClient_) {
100 this.resizeHostDesktop_();
101 }
102
103 // If enabling shrink, reset bump-scroll offsets.
104 var needsScrollReset = shrinkToFit && !this.shrinkToFit_;
105
106 this.shrinkToFit_ = shrinkToFit;
107 this.resizeToClient_ = resizeToClient;
108 this.updateScrollbarVisibility_();
109
110 if (this.hostId_ !== '') {
111 var options = {};
112 options[remoting.ClientSession.KEY_SHRINK_TO_FIT] = this.shrinkToFit_;
113 options[remoting.ClientSession.KEY_RESIZE_TO_CLIENT] = this.resizeToClient_;
114 remoting.HostSettings.save(this.hostId_, options);
115 }
116
117 this.updateDimensions_();
118 if (needsScrollReset) {
119 this.resetScroll_();
120 }
121 };
122
123 /**
124 * Scroll the client plugin by the specified amount, keeping it visible.
125 * Note that this is only used in content full-screen mode (not windowed or
126 * browser full-screen modes), where window.scrollBy and the scrollTop and
127 * scrollLeft properties don't work.
128 * @param {number} dx The amount by which to scroll horizontally. Positive to
129 * scroll right; negative to scroll left.
130 * @param {number} dy The amount by which to scroll vertically. Positive to
131 * scroll down; negative to scroll up.
132 * @return {boolean} True if the requested scroll had no effect because both
133 * vertical and horizontal edges of the screen have been reached.
134 */
135 remoting.DesktopViewport.prototype.scroll = function(dx, dy) {
136 /**
137 * Helper function for x- and y-scrolling
138 * @param {number|string} curr The current margin, eg. "10px".
139 * @param {number} delta The requested scroll amount.
140 * @param {number} windowBound The size of the window, in pixels.
141 * @param {number} pluginBound The size of the plugin, in pixels.
142 * @param {{stop: boolean}} stop Reference parameter used to indicate when
143 * the scroll has reached one of the edges and can be stopped in that
144 * direction.
145 * @return {string} The new margin value.
146 */
147 var adjustMargin = function(curr, delta, windowBound, pluginBound, stop) {
148 var minMargin = Math.min(0, windowBound - pluginBound);
149 var result = (curr ? parseFloat(curr) : 0) - delta;
150 result = Math.min(0, Math.max(minMargin, result));
151 stop.stop = (result === 0 || result == minMargin);
152 return result + 'px';
153 };
154
155 var style = this.pluginContainer_.style;
156
157 var stopX = { stop: false };
158 var clientArea = this.getClientArea();
159 style.marginLeft = adjustMargin(
160 style.marginLeft, dx, clientArea.width,
161 this.pluginWidthForBumpScrollTesting || this.pluginElement_.clientWidth,
162 stopX);
163
164 var stopY = { stop: false };
165 style.marginTop = adjustMargin(
166 style.marginTop, dy, clientArea.height,
167 this.pluginHeightForBumpScrollTesting || this.pluginElement_.clientHeight,
168 stopY);
169 return stopX.stop && stopY.stop;
170 };
171
172 remoting.DesktopViewport.prototype.resetScroll_ = function() {
173 this.pluginContainer_.style.marginTop = '0px';
174 this.pluginContainer_.style.marginLeft = '0px';
175 };
176
177 /**
178 * Enable or disable bump-scrolling. When disabling bump scrolling, also reset
179 * the scroll offsets to (0, 0).
180 * @param {boolean=} enable True to enable bump-scrolling, false to disable it.
181 * @private
182 */
183 remoting.DesktopViewport.prototype.enableBumpScroll = function(enable) {
184 if (enable) {
185 this.bumpScroller_ = new remoting.BumpScroller(this);
186 } else {
187 base.dispose(this.bumpScroller_);
188 this.bumpScroller_ = null;
189 this.resetScroll_();
190 }
191 };
192
193 /**
194 * This is a callback that gets called when the window is resized.
195 *
196 * @return {void} Nothing.
197 */
198 remoting.DesktopViewport.prototype.onResize = function() {
199 this.updateDimensions_();
200
201 if (this.resizeTimer_) {
202 window.clearTimeout(this.resizeTimer_);
203 this.resizeTimer_ = null;
204 }
205
206 // Defer notifying the host of the change until the window stops resizing, to
207 // avoid overloading the control channel with notifications.
208 if (this.resizeToClient_) {
209 var kResizeRateLimitMs = 1000;
210 if (this.hostDesktop_.hasResizeRateLimit) {
211 kResizeRateLimitMs = 250;
212 }
213 var clientArea = this.getClientArea();
214 this.resizeTimer_ = window.setTimeout(this.resizeHostDesktop_.bind(this),
215 kResizeRateLimitMs);
216 }
217
218 // If bump-scrolling is enabled, adjust the plugin margins to fully utilize
219 // the new window area.
220 this.resetScroll_();
221 this.updateScrollbarVisibility_();
222 };
223
224 /**
225 * This is a callback that gets called when the plugin notifies us of a change
226 * in the size of the remote desktop.
227 *
228 * @return {void} Nothing.
229 * @private
230 */
231 remoting.DesktopViewport.prototype.onDesktopSizeChanged_ = function() {
232 var dimensions = this.hostDesktop_.getDimensions();
233 console.log('desktop size changed: ' +
234 dimensions.width + 'x' +
235 dimensions.height +' @ ' +
236 dimensions.xDpi + 'x' +
237 dimensions.yDpi + ' DPI');
238 this.updateDimensions_();
239 this.updateScrollbarVisibility_();
240 };
241
242 /**
243 * Called when the window or desktop size or the scaling settings change,
244 * to set the scroll-bar visibility.
245 *
246 * TODO(jamiewalch): crbug.com/252796: Remove this once crbug.com/240772 is
247 * fixed.
248 */
249 remoting.DesktopViewport.prototype.updateScrollbarVisibility_ = function() {
250 // TODO(kelvinp): Remove the check once looking glass no longer depends on
251 // this.
252 if (!this.rootElement_) {
253 return;
254 }
255
256 var needsScrollY = false;
257 var needsScrollX = false;
258 if (!this.shrinkToFit_) {
259 // Determine whether or not horizontal or vertical scrollbars are
260 // required, taking into account their width.
261 var clientArea = this.getClientArea();
262 var hostDesktop = this.hostDesktop_.getDimensions();
263 needsScrollY = clientArea.height < hostDesktop.height;
264 needsScrollX = clientArea.width < hostDesktop.width;
265 var kScrollBarWidth = 16;
266 if (needsScrollX && !needsScrollY) {
267 needsScrollY = clientArea.height - kScrollBarWidth < hostDesktop.height;
268 } else if (!needsScrollX && needsScrollY) {
269 needsScrollX = clientArea.width - kScrollBarWidth < hostDesktop.width;
270 }
271 }
272
273 this.rootElement_.classList.toggle('no-horizontal-scroll', !needsScrollX);
274 this.rootElement_.classList.toggle('no-vertical-scroll', !needsScrollY);
275 };
276
277 /**
278 * Refreshes the plugin's dimensions, taking into account the sizes of the
279 * remote desktop and client window, and the current scale-to-fit setting.
280 *
281 * @return {void} Nothing.
282 */
283 remoting.DesktopViewport.prototype.updateDimensions_ = function() {
284 var hostDesktop = this.hostDesktop_.getDimensions();
285 if (hostDesktop.width === 0 || hostDesktop.height === 0) {
286 return;
287 }
288
289 var clientArea = this.getClientArea();
290
291 // When configured to display a host at its original size, we aim to display
292 // it as close to its physical size as possible, without losing data:
293 // - If client and host have matching DPI, render the host pixel-for-pixel.
294 // - If the host has higher DPI then still render pixel-for-pixel.
295 // - If the host has lower DPI then let Chrome up-scale it to natural size.
296
297 // We specify the plugin dimensions in Density-Independent Pixels, so to
298 // render pixel-for-pixel we need to down-scale the host dimensions by the
299 // devicePixelRatio of the client. To match the host pixel density, we choose
300 // an initial scale factor based on the client devicePixelRatio and host DPI.
301
302 // Determine the effective device pixel ratio of the host, based on DPI.
303 var hostPixelRatioX = Math.ceil(hostDesktop.xDpi / 96);
304 var hostPixelRatioY = Math.ceil(hostDesktop.yDpi / 96);
305 var hostPixelRatio = Math.min(hostPixelRatioX, hostPixelRatioY);
306
307 // Include the desktopScale in the hostPixelRatio before comparing it with
308 // the client devicePixelRatio to determine the "natural" scale to use.
309 hostPixelRatio *= this.desktopScale_;
310
311 // Down-scale by the smaller of the client and host ratios.
312 var scale = 1.0 / Math.min(window.devicePixelRatio, hostPixelRatio);
313
314 if (this.shrinkToFit_) {
315 // Reduce the scale, if necessary, to fit the whole desktop in the window.
316 var scaleFitWidth =
317 Math.min(scale, 1.0 * clientArea.width / hostDesktop.width);
318 var scaleFitHeight =
319 Math.min(scale, 1.0 * clientArea.height / hostDesktop.height);
320 scale = Math.min(scaleFitHeight, scaleFitWidth);
321
322 // If we're running full-screen then try to handle common side-by-side
323 // multi-monitor combinations more intelligently.
324 if (remoting.fullscreen.isActive()) {
325 // If the host has two monitors each the same size as the client then
326 // scale-to-fit will have the desktop occupy only 50% of the client area,
327 // in which case it would be preferable to down-scale less and let the
328 // user bump-scroll around ("scale-and-pan").
329 // Triggering scale-and-pan if less than 65% of the client area would be
330 // used adds enough fuzz to cope with e.g. 1280x800 client connecting to
331 // a (2x1280)x1024 host nicely.
332 // Note that we don't need to account for scrollbars while fullscreen.
333 if (scale <= scaleFitHeight * 0.65) {
334 scale = scaleFitHeight;
335 }
336 if (scale <= scaleFitWidth * 0.65) {
337 scale = scaleFitWidth;
338 }
339 }
340 }
341
342 var pluginWidth = Math.round(hostDesktop.width * scale);
343 var pluginHeight = Math.round(hostDesktop.height * scale);
344
345 // Resize the plugin if necessary.
346 // TODO(wez): Handle high-DPI to high-DPI properly (crbug.com/135089).
347 this.pluginElement_.style.width = pluginWidth + 'px';
348 this.pluginElement_.style.height = pluginHeight + 'px';
349
350 // Position the container.
351 var parentNode = this.pluginElement_.parentNode;
352
353 console.log('plugin dimensions: ' +
354 parentNode.style.left + ',' +
355 parentNode.style.top + '-' +
356 pluginWidth + 'x' + pluginHeight + '.');
357 };
358
359 /**
360 * @return {{width:number, height:number}} The height of the window's client
361 * area. This differs between apps v1 and apps v2 due to the custom window
362 * borders used by the latter.
363 */
364 remoting.DesktopViewport.prototype.getClientArea = function() {
365 return remoting.windowFrame ?
366 remoting.windowFrame.getClientArea() :
367 { 'width': window.innerWidth, 'height': window.innerHeight };
368 };
369
370 /**
371 * Notifies the host of the client's current dimensions and DPI.
372 * Also takes into account per-host scaling factor, if configured.
373 * @private
374 */
375 remoting.DesktopViewport.prototype.resizeHostDesktop_ = function() {
376 var clientArea = this.getClientArea();
377 this.hostDesktop_.resize(clientArea.width * this.desktopScale_,
378 clientArea.height * this.desktopScale_,
379 window.devicePixelRatio);
380 };
381
382 }());
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698