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.
7 * Functions related to the 'host screen' for Chromoting.
12 /** @suppress {duplicate} */
13 var remoting
= remoting
|| {};
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
21 var lastShareWasCancelled_
= false;
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
);
34 * @param {string} token The OAuth access token.
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()),
50 onNatTraversalPolicyChanged_
,
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);
86 updateAccessCodeTimeoutElement_();
87 updateTimeoutStyles_();
88 remoting
.setMode(remoting
.AppMode
.HOST_WAITING_FOR_CONNECTION
);
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
);
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
);
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;
164 remoting
.hostSession
.disconnect();
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.
183 var timerRunning_
= false;
186 * @type {number} The id of the access code expiry countdown timer.
189 var accessCodeTimerId_
= 0;
192 * @type {number} The number of seconds until the access code expires.
195 var accessCodeExpiresIn_
= 0;
198 * The timer callback function, which needs to be visible from the global
202 remoting
.decrementAccessCodeTimeout_ = function() {
203 --accessCodeExpiresIn_
;
204 updateAccessCodeTimeoutElement_();
208 * Stop the access code timeout countdown if it is running.
210 function disableTimeoutCountdown_() {
212 clearInterval(accessCodeTimerId_
);
213 timerRunning_
= false;
214 updateTimeoutStyles_();
219 * Constants controlling the access code timer countdown display.
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
229 * @return {boolean} True if the timeout is in progress, false if it has
232 function updateTimeoutStyles_() {
234 if (accessCodeExpiresIn_
<= 0) {
235 remoting
.cancelShare();
238 var accessCode
= document
.getElementById('access-code-display');
239 if (accessCodeExpiresIn_
<= ACCESS_CODE_RED_THRESHOLD_
) {
240 accessCode
.classList
.add('expiring');
242 accessCode
.classList
.remove('expiring');
245 document
.getElementById('access-code-countdown').hidden
=
246 (accessCodeExpiresIn_
> ACCESS_CODE_TIMER_DISPLAY_THRESHOLD_
) ||
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');
272 natBox
.classList
.add('traversal-enabled');
274 natBox
.classList
.remove('traversal-enabled');