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

Unified 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 side-by-side diff with in-line comments
Download patch
Index: remoting/webapp/crd/js/desktop_viewport.js
diff --git a/remoting/webapp/crd/js/desktop_viewport.js b/remoting/webapp/crd/js/desktop_viewport.js
new file mode 100644
index 0000000000000000000000000000000000000000..b2638ba4e0d0cdc97cd6a764e126f1a37948fabc
--- /dev/null
+++ b/remoting/webapp/crd/js/desktop_viewport.js
@@ -0,0 +1,382 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+/**
+ * @fileoverview
+ * Provides view port management utilities below for a desktop remoting session.
+ * - Enabling bump scrolling
+ * - Resizing the viewport to fit the host desktop
+ * - Resizing the host desktop to fit the client viewport.
+ */
+
+/** @suppress {duplicate} */
+var remoting = remoting || {};
+
+(function() {
+
+'use strict';
+
+/**
+ * @param {HTMLElement} rootElement (The scroller element)
garykac 2015/02/12 18:18:22 scroller element?
kelvinp 2015/02/12 23:26:41 Done.
+ * @param {remoting.ClientPlugin.HostDesktop} hostDesktop
+ * @param {string} hostId ID of the host.
+ *
+ * TODO(kelvinp): Remove hostId from the constructor params once we can
+ * better encapsulate the host setting interface.
+ * @constructor
+ * @implements {base.Disposable}
+ */
+remoting.DesktopViewport = function(rootElement, hostDesktop, hostId) {
+ /** @private */
+ this.rootElement_ = rootElement;
+ /** @private */
+ // TODO(kelvinp): Query the container by class name instead of id.
+ this.pluginContainer_ = rootElement.querySelector('#client-container');
+ /** @private */
+ this.pluginElement_ = rootElement.querySelector('embed');
+ /** @private */
+ this.hostDesktop_ = hostDesktop;
+ /** @private */
+ this.shrinkToFit_ = true;
+ /** @private */
+ this.resizeToClient_ = true;
+ /** @private */
+ this.desktopScale_ = 1.0;
+ /** @private */
+ this.hostId_ = hostId;
+ /** @private {number?} */
+ this.resizeTimer_ = null;
+ /** @private {remoting.BumpScroller} */
+ this.bumpScroller_ = null;
+ // Bump-scroll test variables. Override to use a fake value for the width
+ // and height of the client plugin so that bump-scrolling can be tested
+ // without relying on the actual size of the host desktop.
+ /** @private {number} */
+ this.pluginWidthForBumpScrollTesting = 0;
+ /** @private {number} */
+ this.pluginHeightForBumpScrollTesting = 0;
+
+ this.eventHook_ = new base.EventHook(
+ this.hostDesktop_, remoting.ClientPlugin.HostDesktop.Events.sizeChanged,
+ this.onDesktopSizeChanged_.bind(this));
+};
+
+remoting.DesktopViewport.prototype.dispose = function() {
+ base.dispose(this.eventHook_);
+ this.eventHook_ = null;
+};
+
+/**
+ * @return {boolean} True if shrink-to-fit is enabled; false otherwise.
+ */
+remoting.DesktopViewport.prototype.getShrinkToFit = function() {
+ return this.shrinkToFit_;
+};
+
+/**
+ * @return {boolean} True if resize-to-client is enabled; false otherwise.
+ */
+remoting.DesktopViewport.prototype.getResizeToClient = function() {
+ return this.resizeToClient_;
+};
+
+/**
+ * Set the shrink-to-fit and resize-to-client flags and save them if this is
+ * a Me2Me connection.
+ *
+ * @param {boolean} shrinkToFit True if the remote desktop should be scaled
+ * down if it is larger than the client window; false if scroll-bars
+ * should be added in this case.
+ * @param {boolean} resizeToClient True if window resizes should cause the
+ * host to attempt to resize its desktop to match the client window size;
+ * false to disable this behaviour for subsequent window resizes--the
+ * current host desktop size is not restored in this case.
+ * @return {void} Nothing.
+ */
+remoting.DesktopViewport.prototype.setScreenMode =
+ function(shrinkToFit, resizeToClient) {
+ if (resizeToClient && !this.resizeToClient_) {
+ this.resizeHostDesktop_();
+ }
+
+ // If enabling shrink, reset bump-scroll offsets.
+ var needsScrollReset = shrinkToFit && !this.shrinkToFit_;
+
+ this.shrinkToFit_ = shrinkToFit;
+ this.resizeToClient_ = resizeToClient;
+ this.updateScrollbarVisibility_();
+
+ if (this.hostId_ !== '') {
+ var options = {};
+ options[remoting.ClientSession.KEY_SHRINK_TO_FIT] = this.shrinkToFit_;
+ options[remoting.ClientSession.KEY_RESIZE_TO_CLIENT] = this.resizeToClient_;
+ remoting.HostSettings.save(this.hostId_, options);
+ }
+
+ this.updateDimensions_();
+ if (needsScrollReset) {
+ this.resetScroll_();
+ }
+};
+
+/**
+ * Scroll the client plugin by the specified amount, keeping it visible.
+ * Note that this is only used in content full-screen mode (not windowed or
+ * browser full-screen modes), where window.scrollBy and the scrollTop and
+ * scrollLeft properties don't work.
+ * @param {number} dx The amount by which to scroll horizontally. Positive to
+ * scroll right; negative to scroll left.
+ * @param {number} dy The amount by which to scroll vertically. Positive to
+ * scroll down; negative to scroll up.
+ * @return {boolean} True if the requested scroll had no effect because both
+ * vertical and horizontal edges of the screen have been reached.
+ */
+remoting.DesktopViewport.prototype.scroll = function(dx, dy) {
+ /**
+ * Helper function for x- and y-scrolling
+ * @param {number|string} curr The current margin, eg. "10px".
+ * @param {number} delta The requested scroll amount.
+ * @param {number} windowBound The size of the window, in pixels.
+ * @param {number} pluginBound The size of the plugin, in pixels.
+ * @param {{stop: boolean}} stop Reference parameter used to indicate when
+ * the scroll has reached one of the edges and can be stopped in that
+ * direction.
+ * @return {string} The new margin value.
+ */
+ var adjustMargin = function(curr, delta, windowBound, pluginBound, stop) {
+ var minMargin = Math.min(0, windowBound - pluginBound);
+ var result = (curr ? parseFloat(curr) : 0) - delta;
+ result = Math.min(0, Math.max(minMargin, result));
+ stop.stop = (result === 0 || result == minMargin);
+ return result + 'px';
+ };
+
+ var style = this.pluginContainer_.style;
+
+ var stopX = { stop: false };
+ var clientArea = this.getClientArea();
+ style.marginLeft = adjustMargin(
+ style.marginLeft, dx, clientArea.width,
+ this.pluginWidthForBumpScrollTesting || this.pluginElement_.clientWidth,
+ stopX);
+
+ var stopY = { stop: false };
+ style.marginTop = adjustMargin(
+ style.marginTop, dy, clientArea.height,
+ this.pluginHeightForBumpScrollTesting || this.pluginElement_.clientHeight,
+ stopY);
+ return stopX.stop && stopY.stop;
+};
+
+remoting.DesktopViewport.prototype.resetScroll_ = function() {
+ this.pluginContainer_.style.marginTop = '0px';
+ this.pluginContainer_.style.marginLeft = '0px';
+};
+
+/**
+ * Enable or disable bump-scrolling. When disabling bump scrolling, also reset
+ * the scroll offsets to (0, 0).
+ * @param {boolean=} enable True to enable bump-scrolling, false to disable it.
+ * @private
+ */
+remoting.DesktopViewport.prototype.enableBumpScroll = function(enable) {
+ if (enable) {
+ this.bumpScroller_ = new remoting.BumpScroller(this);
+ } else {
+ base.dispose(this.bumpScroller_);
+ this.bumpScroller_ = null;
+ this.resetScroll_();
+ }
+};
+
+/**
+ * This is a callback that gets called when the window is resized.
+ *
+ * @return {void} Nothing.
+ */
+remoting.DesktopViewport.prototype.onResize = function() {
+ this.updateDimensions_();
+
+ if (this.resizeTimer_) {
+ window.clearTimeout(this.resizeTimer_);
+ this.resizeTimer_ = null;
+ }
+
+ // Defer notifying the host of the change until the window stops resizing, to
+ // avoid overloading the control channel with notifications.
+ if (this.resizeToClient_) {
+ var kResizeRateLimitMs = 1000;
+ if (this.hostDesktop_.hasResizeRateLimit) {
+ kResizeRateLimitMs = 250;
+ }
+ var clientArea = this.getClientArea();
+ this.resizeTimer_ = window.setTimeout(this.resizeHostDesktop_.bind(this),
+ kResizeRateLimitMs);
+ }
+
+ // If bump-scrolling is enabled, adjust the plugin margins to fully utilize
+ // the new window area.
+ this.resetScroll_();
+ this.updateScrollbarVisibility_();
+};
+
+/**
+ * This is a callback that gets called when the plugin notifies us of a change
+ * in the size of the remote desktop.
+ *
+ * @return {void} Nothing.
+ * @private
+ */
+remoting.DesktopViewport.prototype.onDesktopSizeChanged_ = function() {
+ var dimensions = this.hostDesktop_.getDimensions();
+ console.log('desktop size changed: ' +
+ dimensions.width + 'x' +
+ dimensions.height +' @ ' +
+ dimensions.xDpi + 'x' +
+ dimensions.yDpi + ' DPI');
+ this.updateDimensions_();
+ this.updateScrollbarVisibility_();
+};
+
+/**
+ * Called when the window or desktop size or the scaling settings change,
+ * to set the scroll-bar visibility.
+ *
+ * TODO(jamiewalch): crbug.com/252796: Remove this once crbug.com/240772 is
+ * fixed.
+ */
+remoting.DesktopViewport.prototype.updateScrollbarVisibility_ = function() {
+ // TODO(kelvinp): Remove the check once looking glass no longer depends on
+ // this.
+ if (!this.rootElement_) {
+ return;
+ }
+
+ var needsScrollY = false;
+ var needsScrollX = false;
+ if (!this.shrinkToFit_) {
+ // Determine whether or not horizontal or vertical scrollbars are
+ // required, taking into account their width.
+ var clientArea = this.getClientArea();
+ var hostDesktop = this.hostDesktop_.getDimensions();
+ needsScrollY = clientArea.height < hostDesktop.height;
+ needsScrollX = clientArea.width < hostDesktop.width;
+ var kScrollBarWidth = 16;
+ if (needsScrollX && !needsScrollY) {
+ needsScrollY = clientArea.height - kScrollBarWidth < hostDesktop.height;
+ } else if (!needsScrollX && needsScrollY) {
+ needsScrollX = clientArea.width - kScrollBarWidth < hostDesktop.width;
+ }
+ }
+
+ this.rootElement_.classList.toggle('no-horizontal-scroll', !needsScrollX);
+ this.rootElement_.classList.toggle('no-vertical-scroll', !needsScrollY);
+};
+
+/**
+ * Refreshes the plugin's dimensions, taking into account the sizes of the
+ * remote desktop and client window, and the current scale-to-fit setting.
+ *
+ * @return {void} Nothing.
+ */
+remoting.DesktopViewport.prototype.updateDimensions_ = function() {
+ var hostDesktop = this.hostDesktop_.getDimensions();
+ if (hostDesktop.width === 0 || hostDesktop.height === 0) {
+ return;
+ }
+
+ var clientArea = this.getClientArea();
+
+ // When configured to display a host at its original size, we aim to display
+ // it as close to its physical size as possible, without losing data:
+ // - If client and host have matching DPI, render the host pixel-for-pixel.
+ // - If the host has higher DPI then still render pixel-for-pixel.
+ // - If the host has lower DPI then let Chrome up-scale it to natural size.
+
+ // We specify the plugin dimensions in Density-Independent Pixels, so to
+ // render pixel-for-pixel we need to down-scale the host dimensions by the
+ // devicePixelRatio of the client. To match the host pixel density, we choose
+ // an initial scale factor based on the client devicePixelRatio and host DPI.
+
+ // Determine the effective device pixel ratio of the host, based on DPI.
+ var hostPixelRatioX = Math.ceil(hostDesktop.xDpi / 96);
+ var hostPixelRatioY = Math.ceil(hostDesktop.yDpi / 96);
+ var hostPixelRatio = Math.min(hostPixelRatioX, hostPixelRatioY);
+
+ // Include the desktopScale in the hostPixelRatio before comparing it with
+ // the client devicePixelRatio to determine the "natural" scale to use.
+ hostPixelRatio *= this.desktopScale_;
+
+ // Down-scale by the smaller of the client and host ratios.
+ var scale = 1.0 / Math.min(window.devicePixelRatio, hostPixelRatio);
+
+ if (this.shrinkToFit_) {
+ // Reduce the scale, if necessary, to fit the whole desktop in the window.
+ var scaleFitWidth =
+ Math.min(scale, 1.0 * clientArea.width / hostDesktop.width);
+ var scaleFitHeight =
+ Math.min(scale, 1.0 * clientArea.height / hostDesktop.height);
+ scale = Math.min(scaleFitHeight, scaleFitWidth);
+
+ // If we're running full-screen then try to handle common side-by-side
+ // multi-monitor combinations more intelligently.
+ if (remoting.fullscreen.isActive()) {
+ // If the host has two monitors each the same size as the client then
+ // scale-to-fit will have the desktop occupy only 50% of the client area,
+ // in which case it would be preferable to down-scale less and let the
+ // user bump-scroll around ("scale-and-pan").
+ // Triggering scale-and-pan if less than 65% of the client area would be
+ // used adds enough fuzz to cope with e.g. 1280x800 client connecting to
+ // a (2x1280)x1024 host nicely.
+ // Note that we don't need to account for scrollbars while fullscreen.
+ if (scale <= scaleFitHeight * 0.65) {
+ scale = scaleFitHeight;
+ }
+ if (scale <= scaleFitWidth * 0.65) {
+ scale = scaleFitWidth;
+ }
+ }
+ }
+
+ var pluginWidth = Math.round(hostDesktop.width * scale);
+ var pluginHeight = Math.round(hostDesktop.height * scale);
+
+ // Resize the plugin if necessary.
+ // TODO(wez): Handle high-DPI to high-DPI properly (crbug.com/135089).
+ this.pluginElement_.style.width = pluginWidth + 'px';
+ this.pluginElement_.style.height = pluginHeight + 'px';
+
+ // Position the container.
+ var parentNode = this.pluginElement_.parentNode;
+
+ console.log('plugin dimensions: ' +
+ parentNode.style.left + ',' +
+ parentNode.style.top + '-' +
+ pluginWidth + 'x' + pluginHeight + '.');
+};
+
+/**
+ * @return {{width:number, height:number}} The height of the window's client
+ * area. This differs between apps v1 and apps v2 due to the custom window
+ * borders used by the latter.
+ */
+remoting.DesktopViewport.prototype.getClientArea = function() {
+ return remoting.windowFrame ?
+ remoting.windowFrame.getClientArea() :
+ { 'width': window.innerWidth, 'height': window.innerHeight };
+};
+
+/**
+ * Notifies the host of the client's current dimensions and DPI.
+ * Also takes into account per-host scaling factor, if configured.
+ * @private
+ */
+remoting.DesktopViewport.prototype.resizeHostDesktop_ = function() {
+ var clientArea = this.getClientArea();
+ this.hostDesktop_.resize(clientArea.width * this.desktopScale_,
+ clientArea.height * this.desktopScale_,
+ window.devicePixelRatio);
+};
+
+}());

Powered by Google App Engine
This is Rietveld 408576698