Remove getCached* methods from remoting.Identity.
[chromium-blink-merge.git] / remoting / webapp / crd / js / host_screen.js
blobcb3695d0c272ee5445647a09690325cc4f674abb
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 /** @suppress {duplicate} */
11 var remoting = remoting || {};
13 (function(){
15 'use strict';
17 /** @type {remoting.HostSession} */
18 var hostSession_ = null;
20 /**
21  * @type {boolean} Whether or not the last share was cancelled by the user.
22  *     This controls what screen is shown when the host signals completion.
23  */
24 var lastShareWasCancelled_ = false;
26 /**
27  * Start a host session. This is the main entry point for the host screen,
28  * called directly from the onclick action of a button on the home screen.
29  * It first verifies that the native host components are installed and asks
30  * to install them if necessary.
31  */
32 remoting.tryShare = function() {
33   /** @type {remoting.It2MeHostFacade} */
34   var hostFacade = new remoting.It2MeHostFacade();
36   /** @type {remoting.HostInstallDialog} */
37   var hostInstallDialog = null;
39   var tryInitializeFacade = function() {
40     hostFacade.initialize(onFacadeInitialized, onFacadeInitializationFailed);
41   };
43   var onFacadeInitialized = function () {
44     // Host already installed.
45     remoting.startHostUsingFacade_(hostFacade);
46   };
48   var onFacadeInitializationFailed = function() {
49     // If we failed to initialize the dispatcher then prompt the user to install
50     // the host manually.
51     var hasHostDialog = (hostInstallDialog !== null);  /** jscompile hack */
52     if (!hasHostDialog) {
53       hostInstallDialog = new remoting.HostInstallDialog();
54       hostInstallDialog.show(tryInitializeFacade, onInstallError);
55     } else {
56       hostInstallDialog.tryAgain();
57     }
58   };
60   /** @param {remoting.Error} error */
61   var onInstallError = function(error) {
62     if (error == remoting.Error.CANCELLED) {
63       remoting.setMode(remoting.AppMode.HOME);
64     } else {
65       showShareError_(error);
66     }
67   };
69   tryInitializeFacade();
72 /**
73  * @param {remoting.It2MeHostFacade} hostFacade An initialized It2MeHostFacade.
74  */
75 remoting.startHostUsingFacade_ = function(hostFacade) {
76   console.log('Attempting to share...');
77   remoting.identity.getToken().then(
78       remoting.tryShareWithToken_.bind(null, hostFacade),
79       remoting.Error.handler(remoting.showErrorMessage));
82 /**
83  * @param {remoting.It2MeHostFacade} hostFacade An initialized
84  *     It2MeHostFacade.
85  * @param {string} token The OAuth access token.
86  * @private
87  */
88 remoting.tryShareWithToken_ = function(hostFacade, token) {
89   lastShareWasCancelled_ = false;
90   onNatTraversalPolicyChanged_(true);  // Hide warning by default.
91   remoting.setMode(remoting.AppMode.HOST_WAITING_FOR_CODE);
92   document.getElementById('cancel-share-button').disabled = false;
93   disableTimeoutCountdown_();
95   base.debug.assert(hostSession_ === null);
96   hostSession_ = new remoting.HostSession();
97   remoting.identity.getEmail().then(
98       function(/** string */ email) {
99         hostSession_.connect(
100             hostFacade, email, token, onHostStateChanged_,
101             onNatTraversalPolicyChanged_, logDebugInfo_, it2meConnectFailed_);
102       });
106  * Callback for the host plugin to notify the web app of state changes.
107  * @param {remoting.HostSession.State} state The new state of the plugin.
108  * @return {void} Nothing.
109  */
110 function onHostStateChanged_(state) {
111   if (state == remoting.HostSession.State.STARTING) {
112     // Nothing to do here.
113     console.log('Host state: STARTING');
115   } else if (state == remoting.HostSession.State.REQUESTED_ACCESS_CODE) {
116     // Nothing to do here.
117     console.log('Host state: REQUESTED_ACCESS_CODE');
119   } else if (state == remoting.HostSession.State.RECEIVED_ACCESS_CODE) {
120     console.log('Host state: RECEIVED_ACCESS_CODE');
121     var accessCode = hostSession_.getAccessCode();
122     var accessCodeDisplay = document.getElementById('access-code-display');
123     accessCodeDisplay.innerText = '';
124     // Display the access code in groups of four digits for readability.
125     var kDigitsPerGroup = 4;
126     for (var i = 0; i < accessCode.length; i += kDigitsPerGroup) {
127       var nextFourDigits = document.createElement('span');
128       nextFourDigits.className = 'access-code-digit-group';
129       nextFourDigits.innerText = accessCode.substring(i, i + kDigitsPerGroup);
130       accessCodeDisplay.appendChild(nextFourDigits);
131     }
132     accessCodeExpiresIn_ = hostSession_.getAccessCodeLifetime();
133     if (accessCodeExpiresIn_ > 0) {  // Check it hasn't expired.
134       accessCodeTimerId_ = setInterval(decrementAccessCodeTimeout_, 1000);
135       timerRunning_ = true;
136       updateAccessCodeTimeoutElement_();
137       updateTimeoutStyles_();
138       remoting.setMode(remoting.AppMode.HOST_WAITING_FOR_CONNECTION);
139     } else {
140       // This can only happen if the cloud tells us that the code lifetime is
141       // <= 0s, which shouldn't happen so we don't care how clean this UX is.
142       console.error('Access code already invalid on receipt!');
143       remoting.cancelShare();
144     }
146   } else if (state == remoting.HostSession.State.CONNECTED) {
147     console.log('Host state: CONNECTED');
148     var element = document.getElementById('host-shared-message');
149     var client = hostSession_.getClient();
150     l10n.localizeElement(element, client);
151     remoting.setMode(remoting.AppMode.HOST_SHARED);
152     disableTimeoutCountdown_();
154   } else if (state == remoting.HostSession.State.DISCONNECTING) {
155     console.log('Host state: DISCONNECTING');
157   } else if (state == remoting.HostSession.State.DISCONNECTED) {
158     console.log('Host state: DISCONNECTED');
159     if (remoting.currentMode != remoting.AppMode.HOST_SHARE_FAILED) {
160       // If an error is being displayed, then the plugin should not be able to
161       // hide it by setting the state. Errors must be dismissed by the user
162       // clicking OK, which puts the app into mode HOME.
163       if (lastShareWasCancelled_) {
164         remoting.setMode(remoting.AppMode.HOME);
165       } else {
166         remoting.setMode(remoting.AppMode.HOST_SHARE_FINISHED);
167       }
168     }
169     cleanUp();
170   } else if (state == remoting.HostSession.State.ERROR) {
171     console.error('Host state: ERROR');
172     showShareError_(remoting.Error.UNEXPECTED);
173   } else if (state == remoting.HostSession.State.INVALID_DOMAIN_ERROR) {
174     console.error('Host state: INVALID_DOMAIN_ERROR');
175     showShareError_(remoting.Error.INVALID_HOST_DOMAIN);
176   } else {
177     console.error('Unknown state -> ' + state);
178   }
182  * This is the callback that the host plugin invokes to indicate that there
183  * is additional debug log info to display.
184  * @param {string} msg The message (which will not be localized) to be logged.
185  */
186 function logDebugInfo_(msg) {
187   console.log('plugin: ' + msg);
191  * Show a host-side error message.
193  * @param {string} errorTag The error message to be localized and displayed.
194  * @return {void} Nothing.
195  */
196 function showShareError_(errorTag) {
197   var errorDiv = document.getElementById('host-plugin-error');
198   l10n.localizeElementFromTag(errorDiv, errorTag);
199   console.error('Sharing error: ' + errorTag);
200   remoting.setMode(remoting.AppMode.HOST_SHARE_FAILED);
201   cleanUp();
205  * Show a sharing error with error code UNEXPECTED .
207  * @return {void} Nothing.
208  */
209 function it2meConnectFailed_() {
210   // TODO (weitaosu): Instruct the user to install the native messaging host.
211   // We probably want to add a new error code (with the corresponding error
212   // message for sharing error.
213   console.error('Cannot share desktop.');
214   showShareError_(remoting.Error.UNEXPECTED);
217 function cleanUp() {
218   base.dispose(hostSession_);
219   hostSession_ = null;
223  * Cancel an active or pending it2me share operation.
225  * @return {void} Nothing.
226  */
227 remoting.cancelShare = function() {
228   document.getElementById('cancel-share-button').disabled = true;
229   console.log('Canceling share...');
230   remoting.lastShareWasCancelled = true;
231   try {
232     hostSession_.disconnect();
233   } catch (/** @type {*} */ error) {
234     console.error('Error disconnecting: ' + error +
235                   '. The host probably crashed.');
236     // TODO(jamiewalch): Clean this up. We should have a class representing
237     // the host plugin, like we do for the client, which should handle crash
238     // reporting and it should use a more detailed error message than the
239     // default 'generic' one. See crbug.com/94624
240     showShareError_(remoting.Error.UNEXPECTED);
241   }
242   disableTimeoutCountdown_();
246  * @type {boolean} Whether or not the access code timeout countdown is running.
247  */
248 var timerRunning_ = false;
251  * @type {number} The id of the access code expiry countdown timer.
252  */
253 var accessCodeTimerId_ = 0;
256  * @type {number} The number of seconds until the access code expires.
257  */
258 var accessCodeExpiresIn_ = 0;
261  * The timer callback function
262  * @return {void} Nothing.
263  */
264 function decrementAccessCodeTimeout_() {
265   --accessCodeExpiresIn_;
266   updateAccessCodeTimeoutElement_();
270  * Stop the access code timeout countdown if it is running.
271  * @return {void} Nothing.
272  */
273 function disableTimeoutCountdown_() {
274   if (timerRunning_) {
275     clearInterval(accessCodeTimerId_);
276     timerRunning_ = false;
277     updateTimeoutStyles_();
278   }
282  * Constants controlling the access code timer countdown display.
283  */
284 var ACCESS_CODE_TIMER_DISPLAY_THRESHOLD_ = 30;
285 var ACCESS_CODE_RED_THRESHOLD_ = 10;
288  * Show/hide or restyle various elements, depending on the remaining countdown
289  * and timer state.
291  * @return {boolean} True if the timeout is in progress, false if it has
292  * expired.
293  */
294 function updateTimeoutStyles_() {
295   if (timerRunning_) {
296     if (accessCodeExpiresIn_ <= 0) {
297       remoting.cancelShare();
298       return false;
299     }
300     var accessCode = document.getElementById('access-code-display');
301     if (accessCodeExpiresIn_ <= ACCESS_CODE_RED_THRESHOLD_) {
302       accessCode.classList.add('expiring');
303     } else {
304       accessCode.classList.remove('expiring');
305     }
306   }
307   document.getElementById('access-code-countdown').hidden =
308       (accessCodeExpiresIn_ > ACCESS_CODE_TIMER_DISPLAY_THRESHOLD_) ||
309       !timerRunning_;
310   return true;
314  * Update the text and appearance of the access code timeout element to
315  * reflect the time remaining.
316  * @return {void} Nothing.
317  */
318 function updateAccessCodeTimeoutElement_() {
319   var pad = (accessCodeExpiresIn_ < 10) ? '0:0' : '0:';
320   l10n.localizeElement(document.getElementById('seconds-remaining'),
321                        pad + accessCodeExpiresIn_);
322   if (!updateTimeoutStyles_()) {
323     disableTimeoutCountdown_();
324   }
328  * Callback to show or hide the NAT traversal warning when the policy changes.
329  * @param {boolean} enabled True if NAT traversal is enabled.
330  * @return {void} Nothing.
331  */
332 function onNatTraversalPolicyChanged_(enabled) {
333   var natBox = document.getElementById('nat-box');
334   if (enabled) {
335     natBox.classList.add('traversal-enabled');
336   } else {
337     natBox.classList.remove('traversal-enabled');
338   }
341 })();