Ignore non-active fullscreen windows for shelf state.
[chromium-blink-merge.git] / remoting / webapp / host_screen.js
blobdf4008b47b682a20b4250704e9bf91a898e1a698
1 // Copyright (c) 2012 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 * Functions related to the 'host screen' for Chromoting.
8 */
10 'use strict';
12 /** @suppress {duplicate} */
13 var remoting = remoting || {};
15 /**
16 * @type {boolean} Whether or not the last share was cancelled by the user.
17 * This controls what screen is shown when the host plugin signals
18 * completion.
19 * @private
21 var lastShareWasCancelled_ = false;
23 /**
24 * Start a host session. This is the main entry point for the host screen,
25 * called directly from the onclick action of a button on the home screen.
27 remoting.tryShare = function() {
28 console.log('Attempting to share...');
29 remoting.identity.callWithToken(remoting.tryShareWithToken_,
30 remoting.showErrorMessage);
33 /**
34 * @param {string} token The OAuth access token.
35 * @private
37 remoting.tryShareWithToken_ = function(token) {
38 lastShareWasCancelled_ = false;
39 onNatTraversalPolicyChanged_(true); // Hide warning by default.
40 remoting.setMode(remoting.AppMode.HOST_WAITING_FOR_CODE);
41 document.getElementById('cancel-share-button').disabled = false;
42 disableTimeoutCountdown_();
44 var div = document.getElementById('host-plugin-container');
45 remoting.hostSession = new remoting.HostSession();
46 remoting.hostSession.createPluginAndConnect(
47 document.getElementById('host-plugin-container'),
48 /** @type {string} */(remoting.identity.getCachedEmail()),
49 token,
50 onNatTraversalPolicyChanged_,
51 onHostStateChanged_,
52 logDebugInfo_);
55 /**
56 * Callback for the host plugin to notify the web app of state changes.
57 * @param {remoting.HostSession.State} state The new state of the plugin.
59 function onHostStateChanged_(state) {
60 if (state == remoting.HostSession.State.STARTING) {
61 // Nothing to do here.
62 console.log('Host plugin state: STARTING');
64 } else if (state == remoting.HostSession.State.REQUESTED_ACCESS_CODE) {
65 // Nothing to do here.
66 console.log('Host plugin state: REQUESTED_ACCESS_CODE');
68 } else if (state == remoting.HostSession.State.RECEIVED_ACCESS_CODE) {
69 console.log('Host plugin state: RECEIVED_ACCESS_CODE');
70 var accessCode = remoting.hostSession.getAccessCode();
71 var accessCodeDisplay = document.getElementById('access-code-display');
72 accessCodeDisplay.innerText = '';
73 // Display the access code in groups of four digits for readability.
74 var kDigitsPerGroup = 4;
75 for (var i = 0; i < accessCode.length; i += kDigitsPerGroup) {
76 var nextFourDigits = document.createElement('span');
77 nextFourDigits.className = 'access-code-digit-group';
78 nextFourDigits.innerText = accessCode.substring(i, i + kDigitsPerGroup);
79 accessCodeDisplay.appendChild(nextFourDigits);
81 accessCodeExpiresIn_ = remoting.hostSession.getAccessCodeLifetime();
82 if (accessCodeExpiresIn_ > 0) { // Check it hasn't expired.
83 accessCodeTimerId_ = setInterval(
84 remoting.decrementAccessCodeTimeout_, 1000);
85 timerRunning_ = true;
86 updateAccessCodeTimeoutElement_();
87 updateTimeoutStyles_();
88 remoting.setMode(remoting.AppMode.HOST_WAITING_FOR_CONNECTION);
89 } else {
90 // This can only happen if the cloud tells us that the code lifetime is
91 // <= 0s, which shouldn't happen so we don't care how clean this UX is.
92 console.error('Access code already invalid on receipt!');
93 remoting.cancelShare();
96 } else if (state == remoting.HostSession.State.CONNECTED) {
97 console.log('Host plugin state: CONNECTED');
98 var element = document.getElementById('host-shared-message');
99 var client = remoting.hostSession.getClient();
100 l10n.localizeElement(element, client);
101 remoting.setMode(remoting.AppMode.HOST_SHARED);
102 disableTimeoutCountdown_();
104 } else if (state == remoting.HostSession.State.DISCONNECTING) {
105 console.log('Host plugin state: DISCONNECTING');
107 } else if (state == remoting.HostSession.State.DISCONNECTED) {
108 console.log('Host plugin state: DISCONNECTED');
109 if (remoting.currentMode != remoting.AppMode.HOST_SHARE_FAILED) {
110 // If an error is being displayed, then the plugin should not be able to
111 // hide it by setting the state. Errors must be dismissed by the user
112 // clicking OK, which puts the app into mode HOME.
113 if (lastShareWasCancelled_) {
114 remoting.setMode(remoting.AppMode.HOME);
115 } else {
116 remoting.setMode(remoting.AppMode.HOST_SHARE_FINISHED);
119 remoting.hostSession.removePlugin();
121 } else if (state == remoting.HostSession.State.ERROR) {
122 console.error('Host plugin state: ERROR');
123 showShareError_(remoting.Error.UNEXPECTED);
124 } else if (state == remoting.HostSession.State.INVALID_DOMAIN_ERROR) {
125 console.error('Host plugin state: INVALID_DOMAIN_ERROR');
126 showShareError_(remoting.Error.INVALID_HOST_DOMAIN);
127 } else {
128 console.error('Unknown state -> ' + state);
133 * This is the callback that the host plugin invokes to indicate that there
134 * is additional debug log info to display.
135 * @param {string} msg The message (which will not be localized) to be logged.
137 function logDebugInfo_(msg) {
138 console.log('plugin: ' + msg);
142 * Show a host-side error message.
144 * @param {string} errorTag The error message to be localized and displayed.
145 * @return {void} Nothing.
147 function showShareError_(errorTag) {
148 var errorDiv = document.getElementById('host-plugin-error');
149 l10n.localizeElementFromTag(errorDiv, errorTag);
150 console.error('Sharing error: ' + errorTag);
151 remoting.setMode(remoting.AppMode.HOST_SHARE_FAILED);
155 * Cancel an active or pending share operation.
157 * @return {void} Nothing.
159 remoting.cancelShare = function() {
160 document.getElementById('cancel-share-button').disabled = true;
161 console.log('Canceling share...');
162 remoting.lastShareWasCancelled = true;
163 try {
164 remoting.hostSession.disconnect();
165 } catch (error) {
166 // Hack to force JSCompiler type-safety.
167 var errorTyped = /** @type {{description: string}} */ error;
168 console.error('Error disconnecting: ' + errorTyped.description +
169 '. The host plugin probably crashed.');
170 // TODO(jamiewalch): Clean this up. We should have a class representing
171 // the host plugin, like we do for the client, which should handle crash
172 // reporting and it should use a more detailed error message than the
173 // default 'generic' one. See crbug.com/94624
174 showShareError_(remoting.Error.UNEXPECTED);
176 disableTimeoutCountdown_();
180 * @type {boolean} Whether or not the access code timeout countdown is running.
181 * @private
183 var timerRunning_ = false;
186 * @type {number} The id of the access code expiry countdown timer.
187 * @private
189 var accessCodeTimerId_ = 0;
192 * @type {number} The number of seconds until the access code expires.
193 * @private
195 var accessCodeExpiresIn_ = 0;
198 * The timer callback function, which needs to be visible from the global
199 * namespace.
200 * @private
202 remoting.decrementAccessCodeTimeout_ = function() {
203 --accessCodeExpiresIn_;
204 updateAccessCodeTimeoutElement_();
208 * Stop the access code timeout countdown if it is running.
210 function disableTimeoutCountdown_() {
211 if (timerRunning_) {
212 clearInterval(accessCodeTimerId_);
213 timerRunning_ = false;
214 updateTimeoutStyles_();
219 * Constants controlling the access code timer countdown display.
220 * @private
222 var ACCESS_CODE_TIMER_DISPLAY_THRESHOLD_ = 30;
223 var ACCESS_CODE_RED_THRESHOLD_ = 10;
226 * Show/hide or restyle various elements, depending on the remaining countdown
227 * and timer state.
229 * @return {boolean} True if the timeout is in progress, false if it has
230 * expired.
232 function updateTimeoutStyles_() {
233 if (timerRunning_) {
234 if (accessCodeExpiresIn_ <= 0) {
235 remoting.cancelShare();
236 return false;
238 var accessCode = document.getElementById('access-code-display');
239 if (accessCodeExpiresIn_ <= ACCESS_CODE_RED_THRESHOLD_) {
240 accessCode.classList.add('expiring');
241 } else {
242 accessCode.classList.remove('expiring');
245 document.getElementById('access-code-countdown').hidden =
246 (accessCodeExpiresIn_ > ACCESS_CODE_TIMER_DISPLAY_THRESHOLD_) ||
247 !timerRunning_;
248 return true;
252 * Update the text and appearance of the access code timeout element to
253 * reflect the time remaining.
255 function updateAccessCodeTimeoutElement_() {
256 var pad = (accessCodeExpiresIn_ < 10) ? '0:0' : '0:';
257 l10n.localizeElement(document.getElementById('seconds-remaining'),
258 pad + accessCodeExpiresIn_);
259 if (!updateTimeoutStyles_()) {
260 disableTimeoutCountdown_();
265 * Callback to show or hide the NAT traversal warning when the policy changes.
266 * @param {boolean} enabled True if NAT traversal is enabled.
267 * @return {void} Nothing.
269 function onNatTraversalPolicyChanged_(enabled) {
270 var natBox = document.getElementById('nat-box');
271 if (enabled) {
272 natBox.classList.add('traversal-enabled');
273 } else {
274 natBox.classList.remove('traversal-enabled');