Roll src/third_party/WebKit 3529d49:06e8485 (svn 202554:202555)
[chromium-blink-merge.git] / remoting / webapp / base / js / connected_view.js
blobda75a66261ad4ec73442ac3592cf6ad54c37f133
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
3 // found in the LICENSE file.
5 /**
6  * @fileoverview
7  * Implements a basic UX control for a connected remoting session.
8  */
10 /** @suppress {duplicate} */
11 var remoting = remoting || {};
13 (function() {
15 'use strict';
17 /**
18  * @param {remoting.ClientPlugin} plugin
19  * @param {HTMLElement} viewportElement
20  * @param {HTMLElement} cursorElement
21  *
22  * @constructor
23  * @implements {base.Disposable}
24  */
25 remoting.ConnectedView = function(plugin, viewportElement, cursorElement) {
26   /** @private */
27   this.viewportElement_ = viewportElement;
29   /** @private */
30   this.plugin_ = plugin;
32   /** @private {!Array<HTMLElement>} */
33   this.focusableElements_ = [];
35   /** @private */
36   this.cursor_ = new remoting.ConnectedView.Cursor(
37       plugin, viewportElement, cursorElement);
39   /** @private {Element} */
40   this.debugRegionContainer_ =
41       viewportElement.querySelector('.debug-region-container');
43   var pluginElement = plugin.element();
45   /** @private */
46   this.disposables_ = new base.Disposables(
47     this.cursor_,
48     new base.DomEventHook(pluginElement, 'blur',
49                           this.onPluginLostFocus_.bind(this), false),
50     new base.DomEventHook(document, 'visibilitychange',
51                           this.onVisibilityChanged_.bind(this), false),
52     new remoting.Clipboard(plugin)
53   );
55   // TODO(wez): Only allow mouse lock if the app has the pointerLock permission.
56   this.plugin_.allowMouseLock();
58   pluginElement.focus();
61 /**
62  * @return {void} Nothing.
63  */
64 remoting.ConnectedView.prototype.dispose = function() {
65   base.dispose(this.disposables_);
66   this.disposables_ = null;
67   this.cursorRender_ = null;
68   this.plugin_ = null;
71 /**
72  * Called when the app window is hidden.
73  * @return {void} Nothing.
74  * @private
75  */
76 remoting.ConnectedView.prototype.onVisibilityChanged_ = function() {
77   var pause = document.hidden;
78   this.plugin_.pauseVideo(pause);
79   this.plugin_.pauseAudio(pause);
82 /**
83  * Callback that the plugin invokes to indicate when the connection is
84  * ready.
85  *
86  * @param {boolean} ready True if the connection is ready.
87  */
88 remoting.ConnectedView.prototype.onConnectionReady = function(ready) {
89   this.viewportElement_.classList.toggle('session-client-inactive', !ready);
92 /**
93  * Callback function called when the plugin element loses focus.
94  * @private
95  */
96 remoting.ConnectedView.prototype.onPluginLostFocus_ = function(event) {
97   // Release all keys to prevent them becoming 'stuck down' on the host.
98   this.plugin_.releaseAllKeys();
100   // Focus should stay on the element, not (for example) the toolbar.
101   // Due to crbug.com/246335, we can't restore the focus immediately,
102   // otherwise the plugin gets confused about whether or not it has focus.
103   var that = this;
104   var delayedCallback = function() {
105     // When the 'blur' event handler is called document.activeElement has not
106     // been set to the correct target. We need to retrieve the target in this
107     // delayedCallback.
108     var target = /** @type {!HTMLElement} */ (document.activeElement);
109     if (!that.isElementFocusable_(target)) {
110       that.plugin_.element().focus();
111     }
112   };
113   window.setTimeout(delayedCallback, 0);
117  * Return focus to the plugin.
118  */
119 remoting.ConnectedView.prototype.returnFocusToPlugin = function() {
120   this.plugin_.element().focus();
124  * Determines whether an element is allowed to grab focus from the plugin.
125  * @param {!HTMLElement} element
126  * @return {boolean}
127  * @private
128  */
129 remoting.ConnectedView.prototype.isElementFocusable_ = function(element) {
130   return this.focusableElements_.indexOf(element) !== -1;
134  * Allow the given element to grab focus from the plugin.
135  * @param {!HTMLElement} element
136  * @return {void}
137  */
138 remoting.ConnectedView.prototype.allowFocus = function(element) {
139   if (this.focusableElements_.indexOf(element) < 0) {
140     this.focusableElements_.push(element);
141   }
145  * Handles dirty region debug messages.
147  * @param {{rects:Array<Array<number>>}} region Dirty region of the latest
148  *     frame.
149  */
150 remoting.ConnectedView.prototype.handleDebugRegion = function(region) {
151   while (this.debugRegionContainer_.firstChild) {
152     this.debugRegionContainer_.removeChild(
153         this.debugRegionContainer_.firstChild);
154   }
155   if (region.rects) {
156     var rects = region.rects;
157     for (var i = 0; i < rects.length; ++i) {
158       var rect = document.createElement('div');
159       rect.classList.add('debug-region-rect');
160       rect.style.left = rects[i][0] + 'px';
161       rect.style.top = rects[i][1] +'px';
162       rect.style.width = rects[i][2] +'px';
163       rect.style.height = rects[i][3] + 'px';
164       this.debugRegionContainer_.appendChild(rect);
165     }
166   }
170  * Enables or disables rendering of dirty regions for debugging.
171  * @param {boolean} enable True to enable rendering.
172  */
173 remoting.ConnectedView.prototype.enableDebugRegion = function(enable) {
174   if (enable) {
175     this.plugin_.setDebugDirtyRegionHandler(this.handleDebugRegion.bind(this));
176   } else {
177     this.plugin_.setDebugDirtyRegionHandler(null);
178   }
182  * @param {remoting.ClientPlugin} plugin
183  * @param {HTMLElement} viewportElement
184  * @param {HTMLElement} cursorElement
186  * @constructor
187  * @implements {base.Disposable}
188  */
189 remoting.ConnectedView.Cursor = function(
190     plugin, viewportElement, cursorElement) {
191   /** @private */
192   this.plugin_ = plugin;
193   /** @private */
194   this.cursorElement_ = cursorElement;
195   /** @private */
196   this.eventHook_ = new base.DomEventHook(
197       viewportElement, 'mousemove', this.onMouseMoved_.bind(this), true);
199   this.plugin_.setMouseCursorHandler(this.onCursorChanged_.bind(this));
202 remoting.ConnectedView.Cursor.prototype.dispose = function() {
203   base.dispose(this.eventHook_);
204   this.eventHook_ = null;
205   this.plugin_.setMouseCursorHandler(
206       /** function(string, string, number) */ base.doNothing);
207   this.plugin_ = null;
211  * @param {string} url
212  * @param {number} hotspotX
213  * @param {number} hotspotY
214  * @private
215  */
216 remoting.ConnectedView.Cursor.prototype.onCursorChanged_ = function(
217     url, hotspotX, hotspotY) {
218   this.cursorElement_.hidden = !url;
219   if (url) {
220     this.cursorElement_.style.marginLeft = '-' + hotspotX + 'px';
221     this.cursorElement_.style.marginTop = '-' + hotspotY + 'px';
222     this.cursorElement_.src = url;
223   }
227  * @param {Event} event
228  * @private
229  */
230 remoting.ConnectedView.Cursor.prototype.onMouseMoved_ = function(event) {
231   this.cursorElement_.style.top = event.offsetY + 'px';
232   this.cursorElement_.style.left = event.offsetX + 'px';
235 })();