Update V8 to version 3.30.4 (based on bleeding_edge revision r24443)
[chromium-blink-merge.git] / remoting / webapp / client_screen.js
blob3b06083e9fd00a874c633a1a7eee5b9b8fcc3687
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 'client screen' for Chromoting.
8 */
10 'use strict';
12 /** @suppress {duplicate} */
13 var remoting = remoting || {};
15 /**
16 * @type {remoting.SessionConnector} The connector object, set when a
17 * connection is initiated.
19 remoting.connector = null;
21 /**
22 * @type {remoting.ClientSession} The client session object, set once the
23 * connector has invoked its onOk callback.
25 remoting.clientSession = null;
27 /**
28 * Initiate an IT2Me connection.
30 remoting.connectIT2Me = function() {
31 remoting.ensureSessionConnector_();
32 var accessCode = document.getElementById('access-code-entry').value;
33 remoting.setMode(remoting.AppMode.CLIENT_CONNECTING);
34 remoting.connector.connectIT2Me(accessCode);
37 /**
38 * Update the remoting client layout in response to a resize event.
40 * @return {void} Nothing.
42 remoting.onResize = function() {
43 if (remoting.clientSession) {
44 remoting.clientSession.onResize();
48 /**
49 * Handle changes in the visibility of the window, for example by pausing video.
51 * @return {void} Nothing.
53 remoting.onVisibilityChanged = function() {
54 if (remoting.clientSession) {
55 remoting.clientSession.pauseVideo(
56 ('hidden' in document) ? document.hidden : document.webkitHidden);
60 /**
61 * Disconnect the remoting client.
63 * @return {void} Nothing.
65 remoting.disconnect = function() {
66 if (!remoting.clientSession) {
67 return;
69 if (remoting.clientSession.getMode() == remoting.ClientSession.Mode.IT2ME) {
70 remoting.setMode(remoting.AppMode.CLIENT_SESSION_FINISHED_IT2ME);
71 } else {
72 remoting.setMode(remoting.AppMode.CLIENT_SESSION_FINISHED_ME2ME);
74 remoting.clientSession.disconnect(remoting.Error.NONE);
75 remoting.clientSession = null;
76 console.log('Disconnected.');
79 /**
80 * Callback function called when the state of the client plugin changes. The
81 * current and previous states are available via the |state| member variable.
83 * @param {remoting.ClientSession.StateEvent} state
85 function onClientStateChange_(state) {
86 switch (state.current) {
87 case remoting.ClientSession.State.CLOSED:
88 console.log('Connection closed by host');
89 if (remoting.clientSession.getMode() ==
90 remoting.ClientSession.Mode.IT2ME) {
91 remoting.setMode(remoting.AppMode.CLIENT_SESSION_FINISHED_IT2ME);
92 remoting.hangoutSessionEvents.raiseEvent(
93 remoting.hangoutSessionEvents.sessionStateChanged,
94 remoting.ClientSession.State.CLOSED);
95 } else {
96 remoting.setMode(remoting.AppMode.CLIENT_SESSION_FINISHED_ME2ME);
98 break;
100 case remoting.ClientSession.State.FAILED:
101 var error = remoting.clientSession.getError();
102 console.error('Client plugin reported connection failed: ' + error);
103 if (error == null) {
104 error = remoting.Error.UNEXPECTED;
106 showConnectError_(error);
107 break;
109 default:
110 console.error('Unexpected client plugin state: ' + state.current);
111 // This should only happen if the web-app and client plugin get out of
112 // sync, so MISSING_PLUGIN is a suitable error.
113 showConnectError_(remoting.Error.MISSING_PLUGIN);
114 break;
117 remoting.clientSession.removeEventListener('stateChanged',
118 onClientStateChange_);
119 remoting.clientSession.cleanup();
120 remoting.clientSession = null;
124 * Show a client-side error message.
126 * @param {remoting.Error} errorTag The error to be localized and
127 * displayed.
128 * @return {void} Nothing.
130 function showConnectError_(errorTag) {
131 console.error('Connection failed: ' + errorTag);
132 var errorDiv = document.getElementById('connect-error-message');
133 l10n.localizeElementFromTag(errorDiv, /** @type {string} */ (errorTag));
134 remoting.accessCode = '';
135 var mode = remoting.clientSession ? remoting.clientSession.getMode()
136 : remoting.connector.getConnectionMode();
137 if (mode == remoting.ClientSession.Mode.IT2ME) {
138 remoting.setMode(remoting.AppMode.CLIENT_CONNECT_FAILED_IT2ME);
139 remoting.hangoutSessionEvents.raiseEvent(
140 remoting.hangoutSessionEvents.sessionStateChanged,
141 remoting.ClientSession.State.FAILED
143 } else {
144 remoting.setMode(remoting.AppMode.CLIENT_CONNECT_FAILED_ME2ME);
149 * Set the text on the buttons shown under the error message so that they are
150 * easy to understand in the case where a successful connection failed, as
151 * opposed to the case where a connection never succeeded.
153 function setConnectionInterruptedButtonsText_() {
154 var button1 = document.getElementById('client-reconnect-button');
155 l10n.localizeElementFromTag(button1, /*i18n-content*/'RECONNECT');
156 button1.removeAttribute('autofocus');
157 var button2 = document.getElementById('client-finished-me2me-button');
158 l10n.localizeElementFromTag(button2, /*i18n-content*/'OK');
159 button2.setAttribute('autofocus', 'autofocus');
163 * Timer callback to update the statistics panel.
165 function updateStatistics_() {
166 if (!remoting.clientSession ||
167 remoting.clientSession.getState() !=
168 remoting.ClientSession.State.CONNECTED) {
169 return;
171 var perfstats = remoting.clientSession.getPerfStats();
172 remoting.stats.update(perfstats);
173 remoting.clientSession.logStatistics(perfstats);
174 // Update the stats once per second.
175 window.setTimeout(updateStatistics_, 1000);
179 * Entry-point for Me2Me connections, handling showing of the host-upgrade nag
180 * dialog if necessary.
182 * @param {string} hostId The unique id of the host.
183 * @return {void} Nothing.
185 remoting.connectMe2Me = function(hostId) {
186 var host = remoting.hostList.getHostForId(hostId);
187 if (!host) {
188 showConnectError_(remoting.Error.HOST_IS_OFFLINE);
189 return;
191 var webappVersion = chrome.runtime.getManifest().version;
192 if (remoting.Host.needsUpdate(host, webappVersion)) {
193 var needsUpdateMessage =
194 document.getElementById('host-needs-update-message');
195 l10n.localizeElementFromTag(needsUpdateMessage,
196 /*i18n-content*/'HOST_NEEDS_UPDATE_TITLE',
197 host.hostName);
198 /** @type {Element} */
199 var connect = document.getElementById('host-needs-update-connect-button');
200 /** @type {Element} */
201 var cancel = document.getElementById('host-needs-update-cancel-button');
202 /** @param {Event} event */
203 var onClick = function(event) {
204 connect.removeEventListener('click', onClick, false);
205 cancel.removeEventListener('click', onClick, false);
206 if (event.target == connect) {
207 remoting.connectMe2MeHostVersionAcknowledged_(host);
208 } else {
209 remoting.setMode(remoting.AppMode.HOME);
212 connect.addEventListener('click', onClick, false);
213 cancel.addEventListener('click', onClick, false);
214 remoting.setMode(remoting.AppMode.CLIENT_HOST_NEEDS_UPGRADE);
215 } else {
216 remoting.connectMe2MeHostVersionAcknowledged_(host);
221 * Shows PIN entry screen localized to include the host name, and registers
222 * a host-specific one-shot event handler for the form submission.
224 * @param {remoting.Host} host The Me2Me host to which to connect.
225 * @return {void} Nothing.
227 remoting.connectMe2MeHostVersionAcknowledged_ = function(host) {
228 remoting.ensureSessionConnector_();
229 remoting.setMode(remoting.AppMode.CLIENT_CONNECTING);
232 * @param {string} tokenUrl Token-issue URL received from the host.
233 * @param {string} scope OAuth scope to request the token for.
234 * @param {string} hostPublicKey Host public key (DER and Base64 encoded).
235 * @param {function(string, string):void} onThirdPartyTokenFetched Callback.
237 var fetchThirdPartyToken = function(
238 tokenUrl, hostPublicKey, scope, onThirdPartyTokenFetched) {
239 var thirdPartyTokenFetcher = new remoting.ThirdPartyTokenFetcher(
240 tokenUrl, hostPublicKey, scope, host.tokenUrlPatterns,
241 onThirdPartyTokenFetched);
242 thirdPartyTokenFetcher.fetchToken();
246 * @param {boolean} supportsPairing
247 * @param {function(string):void} onPinFetched
249 var requestPin = function(supportsPairing, onPinFetched) {
250 /** @type {Element} */
251 var pinForm = document.getElementById('pin-form');
252 /** @type {Element} */
253 var pinCancel = document.getElementById('cancel-pin-entry-button');
254 /** @type {Element} */
255 var rememberPin = document.getElementById('remember-pin');
256 /** @type {Element} */
257 var rememberPinCheckbox = document.getElementById('remember-pin-checkbox');
259 * Event handler for both the 'submit' and 'cancel' actions. Using
260 * a single handler for both greatly simplifies the task of making
261 * them one-shot. If separate handlers were used, each would have
262 * to unregister both itself and the other.
264 * @param {Event} event The click or submit event.
266 var onSubmitOrCancel = function(event) {
267 pinForm.removeEventListener('submit', onSubmitOrCancel, false);
268 pinCancel.removeEventListener('click', onSubmitOrCancel, false);
269 var pinField = document.getElementById('pin-entry');
270 var pin = pinField.value;
271 pinField.value = '';
272 if (event.target == pinForm) {
273 event.preventDefault();
275 // Set the focus away from the password field. This has to be done
276 // before the password field gets hidden, to work around a Blink
277 // clipboard-handling bug - http://crbug.com/281523.
278 document.getElementById('pin-connect-button').focus();
280 remoting.setMode(remoting.AppMode.CLIENT_CONNECTING);
281 onPinFetched(pin);
282 if (/** @type {boolean} */(rememberPinCheckbox.checked)) {
283 /** @type {boolean} */
284 remoting.pairingRequested = true;
286 } else {
287 remoting.setMode(remoting.AppMode.HOME);
290 pinForm.addEventListener('submit', onSubmitOrCancel, false);
291 pinCancel.addEventListener('click', onSubmitOrCancel, false);
292 rememberPin.hidden = !supportsPairing;
293 rememberPinCheckbox.checked = false;
294 var message = document.getElementById('pin-message');
295 l10n.localizeElement(message, host.hostName);
296 remoting.setMode(remoting.AppMode.CLIENT_PIN_PROMPT);
299 /** @param {Object} settings */
300 var connectMe2MeHostSettingsRetrieved = function(settings) {
301 /** @type {string} */
302 var clientId = '';
303 /** @type {string} */
304 var sharedSecret = '';
305 var pairingInfo = /** @type {Object} */ (settings['pairingInfo']);
306 if (pairingInfo) {
307 clientId = /** @type {string} */ (pairingInfo['clientId']);
308 sharedSecret = /** @type {string} */ (pairingInfo['sharedSecret']);
310 remoting.connector.connectMe2Me(host, requestPin, fetchThirdPartyToken,
311 clientId, sharedSecret);
314 remoting.HostSettings.load(host.hostId, connectMe2MeHostSettingsRetrieved);
317 /** @param {remoting.ClientSession} clientSession */
318 remoting.onConnected = function(clientSession) {
319 remoting.clientSession = clientSession;
320 remoting.clientSession.addEventListener('stateChanged', onClientStateChange_);
321 setConnectionInterruptedButtonsText_();
322 document.getElementById('access-code-entry').value = '';
323 remoting.setMode(remoting.AppMode.IN_SESSION);
324 if (!base.isAppsV2()) {
325 remoting.toolbar.center();
326 remoting.toolbar.preview();
328 remoting.clipboard.startSession();
329 updateStatistics_();
330 remoting.hangoutSessionEvents.raiseEvent(
331 remoting.hangoutSessionEvents.sessionStateChanged,
332 remoting.ClientSession.State.CONNECTED
334 if (remoting.pairingRequested) {
336 * @param {string} clientId
337 * @param {string} sharedSecret
339 var onPairingComplete = function(clientId, sharedSecret) {
340 var pairingInfo = {
341 pairingInfo: {
342 clientId: clientId,
343 sharedSecret: sharedSecret
346 remoting.HostSettings.save(remoting.connector.getHostId(), pairingInfo);
347 remoting.connector.updatePairingInfo(clientId, sharedSecret);
349 // Use the platform name as a proxy for the local computer name.
350 // TODO(jamiewalch): Use a descriptive name for the local computer, for
351 // example, its Chrome Sync name.
352 var clientName = '';
353 if (remoting.platformIsMac()) {
354 clientName = 'Mac';
355 } else if (remoting.platformIsWindows()) {
356 clientName = 'Windows';
357 } else if (remoting.platformIsChromeOS()) {
358 clientName = 'ChromeOS';
359 } else if (remoting.platformIsLinux()) {
360 clientName = 'Linux';
361 } else {
362 console.log('Unrecognized client platform. Using navigator.platform.');
363 clientName = navigator.platform;
365 clientSession.requestPairing(clientName, onPairingComplete);
370 * Extension message handler.
372 * @param {string} type The type of the extension message.
373 * @param {string} data The payload of the extension message.
374 * @return {boolean} Return true if the extension message was recognized.
376 remoting.onExtensionMessage = function(type, data) {
377 if (remoting.clientSession) {
378 return remoting.clientSession.handleExtensionMessage(type, data);
380 return false;
384 * Create a session connector if one doesn't already exist.
386 remoting.ensureSessionConnector_ = function() {
387 if (!remoting.connector) {
388 remoting.connector = remoting.SessionConnector.factory.createConnector(
389 document.getElementById('video-container'),
390 remoting.onConnected,
391 showConnectError_, remoting.onExtensionMessage);