summaryrefslogtreecommitdiffstats
path: root/remoting/webapp/host_screen.js
diff options
context:
space:
mode:
Diffstat (limited to 'remoting/webapp/host_screen.js')
-rw-r--r--remoting/webapp/host_screen.js271
1 files changed, 271 insertions, 0 deletions
diff --git a/remoting/webapp/host_screen.js b/remoting/webapp/host_screen.js
new file mode 100644
index 0000000..2af3b84
--- /dev/null
+++ b/remoting/webapp/host_screen.js
@@ -0,0 +1,271 @@
+// Copyright (c) 2011 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+/**
+ * @fileoverview
+ * Functions related to the 'host screen' for Chromoting.
+ */
+
+'use strict';
+
+/** @suppress {duplicate} */
+var remoting = remoting || {};
+
+/**
+ * @type {boolean} Whether or not the last share was cancelled by the user.
+ * This controls what screen is shown when the host plugin signals
+ * completion.
+ * @private
+ */
+var lastShareWasCancelled_ = false;
+
+/**
+ * Start a host session. This is the main entry point for the host screen,
+ * called directly from the onclick action of a button on the home screen.
+ */
+remoting.tryShare = function() {
+ remoting.debug.log('Attempting to share...');
+ lastShareWasCancelled_ = false;
+ if (remoting.oauth2.needsNewAccessToken()) {
+ remoting.debug.log('Refreshing token...');
+ remoting.oauth2.refreshAccessToken(function() {
+ if (remoting.oauth2.needsNewAccessToken()) {
+ // If we still need it, we're going to infinite loop.
+ showShareError_(remoting.Error.AUTHENTICATION_FAILED);
+ throw 'Unable to get access token';
+ }
+ remoting.tryShare();
+ });
+ return;
+ }
+
+ onNatTraversalPolicyChanged_(true); // Hide warning by default.
+ remoting.setMode(remoting.AppMode.HOST_WAITING_FOR_CODE);
+ document.getElementById('cancel-button').disabled = false;
+ disableTimeoutCountdown_();
+
+ var div = document.getElementById('host-plugin-container');
+ remoting.hostSession = new remoting.HostSession();
+ remoting.hostSession.createPluginAndConnect(
+ document.getElementById('host-plugin-container'),
+ /** @type {string} */(remoting.oauth2.getCachedEmail()),
+ remoting.oauth2.getAccessToken(),
+ onNatTraversalPolicyChanged_,
+ onHostStateChanged_,
+ logDebugInfo_);
+};
+
+/**
+ * Callback for the host plugin to notify the web app of state changes.
+ * @param {remoting.HostSession.State} state The new state of the plugin.
+ */
+function onHostStateChanged_(state) {
+ if (state == remoting.HostSession.State.STARTING) {
+ // Nothing to do here.
+ remoting.debug.log('Host plugin state: STARTING');
+
+ } else if (state == remoting.HostSession.State.REQUESTED_ACCESS_CODE) {
+ // Nothing to do here.
+ remoting.debug.log('Host plugin state: REQUESTED_ACCESS_CODE');
+
+ } else if (state == remoting.HostSession.State.RECEIVED_ACCESS_CODE) {
+ remoting.debug.log('Host plugin state: RECEIVED_ACCESS_CODE');
+ var accessCode = remoting.hostSession.getAccessCode();
+ var accessCodeDisplay = document.getElementById('access-code-display');
+ accessCodeDisplay.innerText = '';
+ // Display the access code in groups of four digits for readability.
+ var kDigitsPerGroup = 4;
+ for (var i = 0; i < accessCode.length; i += kDigitsPerGroup) {
+ var nextFourDigits = document.createElement('span');
+ nextFourDigits.className = 'access-code-digit-group';
+ nextFourDigits.innerText = accessCode.substring(i, i + kDigitsPerGroup);
+ accessCodeDisplay.appendChild(nextFourDigits);
+ }
+ accessCodeExpiresIn_ = remoting.hostSession.getAccessCodeLifetime();
+ if (accessCodeExpiresIn_ > 0) { // Check it hasn't expired.
+ accessCodeTimerId_ = setInterval(
+ 'remoting.decrementAccessCodeTimeout_()', 1000);
+ timerRunning_ = true;
+ updateAccessCodeTimeoutElement_();
+ updateTimeoutStyles_();
+ remoting.setMode(remoting.AppMode.HOST_WAITING_FOR_CONNECTION);
+ } else {
+ // This can only happen if the cloud tells us that the code lifetime is
+ // <= 0s, which shouldn't happen so we don't care how clean this UX is.
+ remoting.debug.log('Access code already invalid on receipt!');
+ remoting.cancelShare();
+ }
+
+ } else if (state == remoting.HostSession.State.CONNECTED) {
+ remoting.debug.log('Host plugin state: CONNECTED');
+ var element = document.getElementById('host-shared-message');
+ var client = remoting.hostSession.getClient();
+ l10n.localizeElement(element, client);
+ remoting.setMode(remoting.AppMode.HOST_SHARED);
+ disableTimeoutCountdown_();
+
+ } else if (state == remoting.HostSession.State.DISCONNECTING) {
+ remoting.debug.log('Host plugin state: DISCONNECTING');
+
+ } else if (state == remoting.HostSession.State.DISCONNECTED) {
+ remoting.debug.log('Host plugin state: DISCONNECTED');
+ if (remoting.currentMode != remoting.AppMode.HOST_SHARE_FAILED) {
+ // If an error is being displayed, then the plugin should not be able to
+ // hide it by setting the state. Errors must be dismissed by the user
+ // clicking OK, which puts the app into mode HOME.
+ if (lastShareWasCancelled_) {
+ remoting.setMode(remoting.AppMode.HOME);
+ } else {
+ remoting.setMode(remoting.AppMode.HOST_SHARE_FINISHED);
+ }
+ }
+ remoting.hostSession.removePlugin();
+
+ } else if (state == remoting.HostSession.State.ERROR) {
+ remoting.debug.log('Host plugin state: ERROR');
+ showShareError_(remoting.Error.GENERIC);
+ } else {
+ remoting.debug.log('Unknown state -> ' + state);
+ }
+}
+
+/**
+ * This is the callback that the host plugin invokes to indicate that there
+ * is additional debug log info to display.
+ * @param {string} msg The message (which will not be localized) to be logged.
+ */
+function logDebugInfo_(msg) {
+ remoting.debug.log('plugin: ' + msg);
+}
+
+/**
+ * Show a host-side error message.
+ *
+ * @param {string} errorTag The error message to be localized and displayed.
+ * @return {void} Nothing.
+ */
+function showShareError_(errorTag) {
+ var errorDiv = document.getElementById('host-plugin-error');
+ l10n.localizeElementFromTag(errorDiv, errorTag);
+ remoting.debug.log('Sharing error: ' + errorTag);
+ remoting.setMode(remoting.AppMode.HOST_SHARE_FAILED);
+}
+
+/**
+ * Cancel an active or pending share operation.
+ *
+ * @return {void} Nothing.
+ */
+remoting.cancelShare = function() {
+ remoting.debug.log('Canceling share...');
+ remoting.lastShareWasCancelled = true;
+ try {
+ remoting.hostSession.disconnect();
+ } catch (error) {
+ // Hack to force JSCompiler type-safety.
+ var errorTyped = /** @type {{description: string}} */ error;
+ remoting.debug.log('Error disconnecting: ' + errorTyped.description +
+ '. The host plugin probably crashed.');
+ // TODO(jamiewalch): Clean this up. We should have a class representing
+ // the host plugin, like we do for the client, which should handle crash
+ // reporting and it should use a more detailed error message than the
+ // default 'generic' one. See crbug.com/94624
+ showShareError_(remoting.Error.GENERIC);
+ }
+ disableTimeoutCountdown_();
+};
+
+/**
+ * @type {boolean} Whether or not the access code timeout countdown is running.
+ * @private
+ */
+var timerRunning_ = false;
+
+/**
+ * @type {number} The id of the access code expiry countdown timer.
+ * @private
+ */
+var accessCodeTimerId_ = 0;
+
+/**
+ * @type {number} The number of seconds until the access code expires.
+ * @private
+ */
+var accessCodeExpiresIn_ = 0;
+
+/**
+ * The timer callback function, which needs to be visible from the global
+ * namespace.
+ * @private
+ */
+remoting.decrementAccessCodeTimeout_ = function() {
+ --accessCodeExpiresIn_;
+ updateAccessCodeTimeoutElement_();
+};
+
+/**
+ * Stop the access code timeout countdown if it is running.
+ */
+function disableTimeoutCountdown_() {
+ if (timerRunning_) {
+ clearInterval(accessCodeTimerId_);
+ timerRunning_ = false;
+ updateTimeoutStyles_();
+ }
+}
+
+/**
+ * Constants controlling the access code timer countdown display.
+ * @private
+ */
+var ACCESS_CODE_TIMER_DISPLAY_THRESHOLD_ = 30;
+var ACCESS_CODE_RED_THRESHOLD_ = 10;
+
+/**
+ * Show/hide or restyle various elements, depending on the remaining countdown
+ * and timer state.
+ *
+ * @return {boolean} True if the timeout is in progress, false if it has
+ * expired.
+ */
+function updateTimeoutStyles_() {
+ if (timerRunning_) {
+ if (accessCodeExpiresIn_ <= 0) {
+ remoting.cancelShare();
+ return false;
+ }
+ if (accessCodeExpiresIn_ <= ACCESS_CODE_RED_THRESHOLD_) {
+ addClass(document.getElementById('access-code-display'), 'expiring');
+ } else {
+ removeClass(document.getElementById('access-code-display'), 'expiring');
+ }
+ }
+ document.getElementById('access-code-countdown').hidden =
+ (accessCodeExpiresIn_ > ACCESS_CODE_TIMER_DISPLAY_THRESHOLD_) ||
+ !timerRunning_;
+ return true;
+}
+
+/**
+ * Update the text and appearance of the access code timeout element to
+ * reflect the time remaining.
+ */
+function updateAccessCodeTimeoutElement_() {
+ var pad = (accessCodeExpiresIn_ < 10) ? '0:0' : '0:';
+ l10n.localizeElement(document.getElementById('seconds-remaining'),
+ pad + accessCodeExpiresIn_);
+ if (!updateTimeoutStyles_()) {
+ disableTimeoutCountdown_();
+ }
+}
+
+/**
+ * Callback to show or hide the NAT traversal warning when the policy changes.
+ * @param {boolean} enabled True if NAT traversal is enabled.
+ * @return {void} Nothing.
+ */
+function onNatTraversalPolicyChanged_(enabled) {
+ var container = document.getElementById('nat-box-container');
+ container.hidden = enabled;
+}