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 'client screen' for Chromoting.
12 /** @suppress {duplicate} */
13 var remoting = remoting || {};
16 * @type {remoting.SessionConnector} The connector object, set when a connection
19 remoting.connector = null;
22 * @type {remoting.ClientSession} The client session object, set once the
23 * connector has invoked its onOk callback.
25 remoting.clientSession = null;
28 * Initiate an IT2Me connection.
30 remoting.connectIT2Me = function() {
31 if (!remoting.connector) {
32 remoting.connector = new remoting.SessionConnector(
33 document.getElementById('session-mode'),
37 var accessCode = document.getElementById('access-code-entry').value;
38 remoting.setMode(remoting.AppMode.CLIENT_CONNECTING);
39 remoting.connector.connectIT2Me(accessCode);
43 * Update the remoting client layout in response to a resize event.
45 * @return {void} Nothing.
47 remoting.onResize = function() {
48 if (remoting.clientSession) {
49 remoting.clientSession.onResize();
54 * Handle changes in the visibility of the window, for example by pausing video.
56 * @return {void} Nothing.
58 remoting.onVisibilityChanged = function() {
59 if (remoting.clientSession) {
60 remoting.clientSession.pauseVideo(document.webkitHidden);
65 * Disconnect the remoting client.
67 * @return {void} Nothing.
69 remoting.disconnect = function() {
70 if (!remoting.clientSession) {
73 if (remoting.clientSession.getMode() == remoting.ClientSession.Mode.IT2ME) {
74 remoting.setMode(remoting.AppMode.CLIENT_SESSION_FINISHED_IT2ME);
76 remoting.setMode(remoting.AppMode.CLIENT_SESSION_FINISHED_ME2ME);
78 remoting.clientSession.disconnect(true);
79 remoting.clientSession = null;
80 console.log('Disconnected.');
84 * Sends a Ctrl-Alt-Del sequence to the remoting client.
86 * @return {void} Nothing.
88 remoting.sendCtrlAltDel = function() {
89 if (remoting.clientSession) {
90 console.log('Sending Ctrl-Alt-Del.');
91 remoting.clientSession.sendCtrlAltDel();
96 * Sends a Print Screen keypress to the remoting client.
98 * @return {void} Nothing.
100 remoting.sendPrintScreen = function() {
101 if (remoting.clientSession) {
102 console.log('Sending Print Screen.');
103 remoting.clientSession.sendPrintScreen();
108 * Callback function called when the state of the client plugin changes. The
109 * current state is available via the |state| member variable.
111 * @param {number} oldState The previous state of the plugin.
112 * @param {number} newState The current state of the plugin.
114 function onClientStateChange_(oldState, newState) {
116 case remoting.ClientSession.State.CLOSED:
117 console.log('Connection closed by host');
118 if (remoting.clientSession.getMode() ==
119 remoting.ClientSession.Mode.IT2ME) {
120 remoting.setMode(remoting.AppMode.CLIENT_SESSION_FINISHED_IT2ME);
122 remoting.setMode(remoting.AppMode.CLIENT_SESSION_FINISHED_ME2ME);
126 case remoting.ClientSession.State.FAILED:
127 var error = remoting.clientSession.getError();
128 console.error('Client plugin reported connection failed: ' + error);
130 error = remoting.Error.UNEXPECTED;
132 showConnectError_(error);
136 console.error('Unexpected client plugin state: ' + newState);
137 // This should only happen if the web-app and client plugin get out of
138 // sync, so MISSING_PLUGIN is a suitable error.
139 showConnectError_(remoting.Error.MISSING_PLUGIN);
142 remoting.clientSession.disconnect(false);
143 remoting.clientSession.removePlugin();
144 remoting.clientSession = null;
148 * Show a client-side error message.
150 * @param {remoting.Error} errorTag The error to be localized and
152 * @return {void} Nothing.
154 function showConnectError_(errorTag) {
155 console.error('Connection failed: ' + errorTag);
156 var errorDiv = document.getElementById('connect-error-message');
157 l10n.localizeElementFromTag(errorDiv, /** @type {string} */ (errorTag));
158 remoting.accessCode = '';
159 var mode = remoting.clientSession ? remoting.clientSession.getMode()
160 : remoting.connector.getConnectionMode();
161 if (mode == remoting.ClientSession.Mode.IT2ME) {
162 remoting.setMode(remoting.AppMode.CLIENT_CONNECT_FAILED_IT2ME);
164 remoting.setMode(remoting.AppMode.CLIENT_CONNECT_FAILED_ME2ME);
169 * Set the text on the buttons shown under the error message so that they are
170 * easy to understand in the case where a successful connection failed, as
171 * opposed to the case where a connection never succeeded.
173 function setConnectionInterruptedButtonsText_() {
174 var button1 = document.getElementById('client-reconnect-button');
175 l10n.localizeElementFromTag(button1, /*i18n-content*/'RECONNECT');
176 button1.removeAttribute('autofocus');
177 var button2 = document.getElementById('client-finished-me2me-button');
178 l10n.localizeElementFromTag(button2, /*i18n-content*/'OK');
179 button2.setAttribute('autofocus', 'autofocus');
183 * Timer callback to update the statistics panel.
185 function updateStatistics_() {
186 if (!remoting.clientSession ||
187 remoting.clientSession.getState() !=
188 remoting.ClientSession.State.CONNECTED) {
191 var perfstats = remoting.clientSession.getPerfStats();
192 remoting.stats.update(perfstats);
193 remoting.clientSession.logStatistics(perfstats);
194 // Update the stats once per second.
195 window.setTimeout(updateStatistics_, 1000);
199 * Entry-point for Me2Me connections, handling showing of the host-upgrade nag
200 * dialog if necessary.
202 * @param {string} hostId The unique id of the host.
203 * @return {void} Nothing.
205 remoting.connectMe2Me = function(hostId) {
206 var host = remoting.hostList.getHostForId(hostId);
208 showConnectError_(remoting.Error.HOST_IS_OFFLINE);
211 var webappVersion = chrome.runtime.getManifest().version;
212 if (remoting.Host.needsUpdate(host, webappVersion)) {
213 var needsUpdateMessage =
214 document.getElementById('host-needs-update-message');
215 l10n.localizeElementFromTag(needsUpdateMessage,
216 /*i18n-content*/'HOST_NEEDS_UPDATE_TITLE',
218 /** @type {Element} */
219 var connect = document.getElementById('host-needs-update-connect-button');
220 /** @type {Element} */
221 var cancel = document.getElementById('host-needs-update-cancel-button');
222 /** @param {Event} event */
223 var onClick = function(event) {
224 connect.removeEventListener('click', onClick, false);
225 cancel.removeEventListener('click', onClick, false);
226 if (event.target == connect) {
227 remoting.connectMe2MeHostVersionAcknowledged_(host);
229 remoting.setMode(remoting.AppMode.HOME);
232 connect.addEventListener('click', onClick, false);
233 cancel.addEventListener('click', onClick, false);
234 remoting.setMode(remoting.AppMode.CLIENT_HOST_NEEDS_UPGRADE);
236 remoting.connectMe2MeHostVersionAcknowledged_(host);
241 * Shows PIN entry screen localized to include the host name, and registers
242 * a host-specific one-shot event handler for the form submission.
244 * @param {remoting.Host} host The Me2Me host to which to connect.
245 * @return {void} Nothing.
247 remoting.connectMe2MeHostVersionAcknowledged_ = function(host) {
248 if (!remoting.connector) {
249 remoting.connector = new remoting.SessionConnector(
250 document.getElementById('session-mode'),
251 remoting.onConnected,
254 remoting.setMode(remoting.AppMode.CLIENT_CONNECTING);
257 * @param {string} tokenUrl Token-issue URL received from the host.
258 * @param {string} scope OAuth scope to request the token for.
259 * @param {string} hostPublicKey Host public key (DER and Base64 encoded).
260 * @param {function(string, string):void} onThirdPartyTokenFetched Callback.
262 var fetchThirdPartyToken = function(
263 tokenUrl, hostPublicKey, scope, onThirdPartyTokenFetched) {
264 var thirdPartyTokenFetcher = new remoting.ThirdPartyTokenFetcher(
265 tokenUrl, hostPublicKey, scope, host.tokenUrlPatterns,
266 onThirdPartyTokenFetched);
267 thirdPartyTokenFetcher.fetchToken();
271 * @param {boolean} supportsPairing
272 * @param {function(string):void} onPinFetched
274 var requestPin = function(supportsPairing, onPinFetched) {
275 /** @type {Element} */
276 var pinForm = document.getElementById('pin-form');
277 /** @type {Element} */
278 var pinCancel = document.getElementById('cancel-pin-entry-button');
279 /** @type {Element} */
280 var rememberPin = document.getElementById('remember-pin');
281 /** @type {Element} */
282 var rememberPinCheckbox = document.getElementById('remember-pin-checkbox');
284 * Event handler for both the 'submit' and 'cancel' actions. Using
285 * a single handler for both greatly simplifies the task of making
286 * them one-shot. If separate handlers were used, each would have
287 * to unregister both itself and the other.
289 * @param {Event} event The click or submit event.
291 var onSubmitOrCancel = function(event) {
292 pinForm.removeEventListener('submit', onSubmitOrCancel, false);
293 pinCancel.removeEventListener('click', onSubmitOrCancel, false);
294 var pinField = document.getElementById('pin-entry');
295 var pin = pinField.value;
297 if (event.target == pinForm) {
298 event.preventDefault();
300 // Set the focus away from the password field. This has to be done
301 // before the password field gets hidden, to work around a Blink
302 // clipboard-handling bug - http://crbug.com/281523.
303 document.getElementById('pin-connect-button').focus();
305 remoting.setMode(remoting.AppMode.CLIENT_CONNECTING);
307 if (/** @type {boolean} */(rememberPinCheckbox.checked)) {
308 remoting.connector.pairingRequested = true;
311 remoting.setMode(remoting.AppMode.HOME);
314 pinForm.addEventListener('submit', onSubmitOrCancel, false);
315 pinCancel.addEventListener('click', onSubmitOrCancel, false);
316 rememberPin.hidden = !supportsPairing;
317 rememberPinCheckbox.checked = false;
318 var message = document.getElementById('pin-message');
319 l10n.localizeElement(message, host.hostName);
320 remoting.setMode(remoting.AppMode.CLIENT_PIN_PROMPT);
323 /** @param {Object} settings */
324 var connectMe2MeHostSettingsRetrieved = function(settings) {
325 /** @type {string} */
327 /** @type {string} */
328 var sharedSecret = '';
329 var pairingInfo = /** @type {Object} */ (settings['pairingInfo']);
331 clientId = /** @type {string} */ (pairingInfo['clientId']);
332 sharedSecret = /** @type {string} */ (pairingInfo['sharedSecret']);
334 remoting.connector.connectMe2Me(host, requestPin, fetchThirdPartyToken,
335 clientId, sharedSecret);
338 remoting.HostSettings.load(host.hostId, connectMe2MeHostSettingsRetrieved);
341 /** @param {remoting.ClientSession} clientSession */
342 remoting.onConnected = function(clientSession) {
343 remoting.clientSession = clientSession;
344 remoting.clientSession.setOnStateChange(onClientStateChange_);
345 setConnectionInterruptedButtonsText_();
346 var connectedTo = document.getElementById('connected-to');
347 connectedTo.innerText = remoting.connector.getHostDisplayName();
348 document.getElementById('access-code-entry').value = '';
349 remoting.setMode(remoting.AppMode.IN_SESSION);
350 remoting.toolbar.center();
351 remoting.toolbar.preview();
352 remoting.clipboard.startSession();
354 if (remoting.connector.pairingRequested) {
356 * @param {string} clientId
357 * @param {string} sharedSecret
359 var onPairingComplete = function(clientId, sharedSecret) {
363 sharedSecret: sharedSecret
366 remoting.HostSettings.save(remoting.connector.getHostId(), pairingInfo);
367 remoting.connector.updatePairingInfo(clientId, sharedSecret);
369 // Use the platform name as a proxy for the local computer name.
370 // TODO(jamiewalch): Use a descriptive name for the local computer, for
371 // example, its Chrome Sync name.
373 if (navigator.platform.indexOf('Mac') != -1) {
375 } else if (navigator.platform.indexOf('Win32') != -1) {
376 clientName = 'Windows';
377 } else if (navigator.userAgent.match(/\bCrOS\b/)) {
378 clientName = 'ChromeOS';
379 } else if (navigator.platform.indexOf('Linux') != -1) {
380 clientName = 'Linux';
382 console.log('Unrecognized client platform. Using navigator.platform.');
383 clientName = navigator.platform;
385 clientSession.requestPairing(clientName, onPairingComplete);