[Chromoting] Create core Application interface for CRD.
[chromium-blink-merge.git] / remoting / webapp / crd / js / client_screen.js
blob523dd32352e164a7616981682f48c302190ce316
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.ClientSession} The client session object, set once the
17  *     connector has invoked its onOk callback.
18  */
19 remoting.clientSession = null;
21 /**
22  * Initiate an IT2Me connection.
23  */
24 remoting.connectIT2Me = function() {
25   var connector = remoting.app.getSessionConnector();
26   var accessCode = document.getElementById('access-code-entry').value;
27   remoting.setMode(remoting.AppMode.CLIENT_CONNECTING);
28   connector.connectIT2Me(accessCode);
31 /**
32  * Update the remoting client layout in response to a resize event.
33  *
34  * @return {void} Nothing.
35  */
36 remoting.onResize = function() {
37   if (remoting.clientSession) {
38     remoting.clientSession.onResize();
39   }
42 /**
43  * Handle changes in the visibility of the window, for example by pausing video.
44  *
45  * @return {void} Nothing.
46  */
47 remoting.onVisibilityChanged = function() {
48   if (remoting.clientSession) {
49     remoting.clientSession.pauseVideo(
50       ('hidden' in document) ? document.hidden : document.webkitHidden);
51   }
54 /**
55  * Disconnect the remoting client.
56  *
57  * @return {void} Nothing.
58  */
59 remoting.disconnect = function() {
60   if (!remoting.clientSession) {
61     return;
62   }
63   if (remoting.clientSession.getMode() == remoting.ClientSession.Mode.IT2ME) {
64     remoting.setMode(remoting.AppMode.CLIENT_SESSION_FINISHED_IT2ME);
65   } else {
66     remoting.setMode(remoting.AppMode.CLIENT_SESSION_FINISHED_ME2ME);
67   }
68   remoting.clientSession.disconnect(remoting.Error.NONE);
69   remoting.clientSession = null;
70   console.log('Disconnected.');
73 /**
74  * Callback function called when the state of the client plugin changes. The
75  * current and previous states are available via the |state| member variable.
76  *
77  * @param {remoting.ClientSession.StateEvent} state
78  */
79 function onClientStateChange_(state) {
80   switch (state.current) {
81     case remoting.ClientSession.State.CLOSED:
82       console.log('Connection closed by host');
83       if (remoting.clientSession.getMode() ==
84           remoting.ClientSession.Mode.IT2ME) {
85         remoting.setMode(remoting.AppMode.CLIENT_SESSION_FINISHED_IT2ME);
86         remoting.hangoutSessionEvents.raiseEvent(
87             remoting.hangoutSessionEvents.sessionStateChanged,
88             remoting.ClientSession.State.CLOSED);
89       } else {
90         remoting.setMode(remoting.AppMode.CLIENT_SESSION_FINISHED_ME2ME);
91       }
92       break;
94     case remoting.ClientSession.State.FAILED:
95       var error = remoting.clientSession.getError();
96       console.error('Client plugin reported connection failed: ' + error);
97       if (error == null) {
98         error = remoting.Error.UNEXPECTED;
99       }
100       remoting.app.onError(error);
101       break;
103     default:
104       console.error('Unexpected client plugin state: ' + state.current);
105       // This should only happen if the web-app and client plugin get out of
106       // sync, so MISSING_PLUGIN is a suitable error.
107       remoting.app.onError(remoting.Error.MISSING_PLUGIN);
108       break;
109   }
111   remoting.clientSession.removeEventListener('stateChanged',
112                                              onClientStateChange_);
113   remoting.clientSession.cleanup();
114   remoting.clientSession = null;
118  * Timer callback to update the statistics panel.
119  */
120 function updateStatistics_() {
121   if (!remoting.clientSession ||
122       remoting.clientSession.getState() !=
123       remoting.ClientSession.State.CONNECTED) {
124     return;
125   }
126   var perfstats = remoting.clientSession.getPerfStats();
127   remoting.stats.update(perfstats);
128   remoting.clientSession.logStatistics(perfstats);
129   // Update the stats once per second.
130   window.setTimeout(updateStatistics_, 1000);
134  * Entry-point for Me2Me connections, handling showing of the host-upgrade nag
135  * dialog if necessary.
137  * @param {string} hostId The unique id of the host.
138  * @return {void} Nothing.
139  */
140 remoting.connectMe2Me = function(hostId) {
141   var host = remoting.hostList.getHostForId(hostId);
142   if (!host) {
143     remoting.app.onError(remoting.Error.HOST_IS_OFFLINE);
144     return;
145   }
146   var webappVersion = chrome.runtime.getManifest().version;
147   if (remoting.Host.needsUpdate(host, webappVersion)) {
148     var needsUpdateMessage =
149         document.getElementById('host-needs-update-message');
150     l10n.localizeElementFromTag(needsUpdateMessage,
151                                 /*i18n-content*/'HOST_NEEDS_UPDATE_TITLE',
152                                 host.hostName);
153     /** @type {Element} */
154     var connect = document.getElementById('host-needs-update-connect-button');
155     /** @type {Element} */
156     var cancel = document.getElementById('host-needs-update-cancel-button');
157     /** @param {Event} event */
158     var onClick = function(event) {
159       connect.removeEventListener('click', onClick, false);
160       cancel.removeEventListener('click', onClick, false);
161       if (event.target == connect) {
162         remoting.connectMe2MeHostVersionAcknowledged_(host);
163       } else {
164         remoting.setMode(remoting.AppMode.HOME);
165       }
166     }
167     connect.addEventListener('click', onClick, false);
168     cancel.addEventListener('click', onClick, false);
169     remoting.setMode(remoting.AppMode.CLIENT_HOST_NEEDS_UPGRADE);
170   } else {
171     remoting.connectMe2MeHostVersionAcknowledged_(host);
172   }
176  * Shows PIN entry screen localized to include the host name, and registers
177  * a host-specific one-shot event handler for the form submission.
179  * @param {remoting.Host} host The Me2Me host to which to connect.
180  * @return {void} Nothing.
181  */
182 remoting.connectMe2MeHostVersionAcknowledged_ = function(host) {
183   /** @type {remoting.SessionConnector} */
184   var connector = remoting.app.getSessionConnector();
185   remoting.setMode(remoting.AppMode.CLIENT_CONNECTING);
187   /**
188    * @param {string} tokenUrl Token-issue URL received from the host.
189    * @param {string} scope OAuth scope to request the token for.
190    * @param {string} hostPublicKey Host public key (DER and Base64 encoded).
191    * @param {function(string, string):void} onThirdPartyTokenFetched Callback.
192    */
193   var fetchThirdPartyToken = function(
194       tokenUrl, hostPublicKey, scope, onThirdPartyTokenFetched) {
195     var thirdPartyTokenFetcher = new remoting.ThirdPartyTokenFetcher(
196         tokenUrl, hostPublicKey, scope, host.tokenUrlPatterns,
197         onThirdPartyTokenFetched);
198     thirdPartyTokenFetcher.fetchToken();
199   };
201   /**
202    * @param {boolean} supportsPairing
203    * @param {function(string):void} onPinFetched
204    */
205   var requestPin = function(supportsPairing, onPinFetched) {
206     /** @type {Element} */
207     var pinForm = document.getElementById('pin-form');
208     /** @type {Element} */
209     var pinCancel = document.getElementById('cancel-pin-entry-button');
210     /** @type {Element} */
211     var rememberPin = document.getElementById('remember-pin');
212     /** @type {Element} */
213     var rememberPinCheckbox = document.getElementById('remember-pin-checkbox');
214     /**
215      * Event handler for both the 'submit' and 'cancel' actions. Using
216      * a single handler for both greatly simplifies the task of making
217      * them one-shot. If separate handlers were used, each would have
218      * to unregister both itself and the other.
219      *
220      * @param {Event} event The click or submit event.
221      */
222     var onSubmitOrCancel = function(event) {
223       pinForm.removeEventListener('submit', onSubmitOrCancel, false);
224       pinCancel.removeEventListener('click', onSubmitOrCancel, false);
225       var pinField = document.getElementById('pin-entry');
226       var pin = pinField.value;
227       pinField.value = '';
228       if (event.target == pinForm) {
229         event.preventDefault();
231         // Set the focus away from the password field. This has to be done
232         // before the password field gets hidden, to work around a Blink
233         // clipboard-handling bug - http://crbug.com/281523.
234         document.getElementById('pin-connect-button').focus();
236         remoting.setMode(remoting.AppMode.CLIENT_CONNECTING);
237         onPinFetched(pin);
238         if (/** @type {boolean} */(rememberPinCheckbox.checked)) {
239           /** @type {boolean} */
240           remoting.pairingRequested = true;
241         }
242       } else {
243         remoting.setMode(remoting.AppMode.HOME);
244       }
245     };
246     pinForm.addEventListener('submit', onSubmitOrCancel, false);
247     pinCancel.addEventListener('click', onSubmitOrCancel, false);
248     rememberPin.hidden = !supportsPairing;
249     rememberPinCheckbox.checked = false;
250     var message = document.getElementById('pin-message');
251     l10n.localizeElement(message, host.hostName);
252     remoting.setMode(remoting.AppMode.CLIENT_PIN_PROMPT);
253   };
255   /** @param {Object} settings */
256   var connectMe2MeHostSettingsRetrieved = function(settings) {
257     /** @type {string} */
258     var clientId = '';
259     /** @type {string} */
260     var sharedSecret = '';
261     var pairingInfo = /** @type {Object} */ (settings['pairingInfo']);
262     if (pairingInfo) {
263       clientId = /** @type {string} */ (pairingInfo['clientId']);
264       sharedSecret = /** @type {string} */ (pairingInfo['sharedSecret']);
265     }
266     connector.connectMe2Me(host, requestPin, fetchThirdPartyToken,
267                                     clientId, sharedSecret);
268   }
270   remoting.HostSettings.load(host.hostId, connectMe2MeHostSettingsRetrieved);