| OLD | NEW |
| (Empty) |
| 1 // Copyright (c) 2011 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. | |
| 4 | |
| 5 /** | |
| 6 * @fileoverview | |
| 7 * Functions related to the 'host screen' for Chromoting. | |
| 8 */ | |
| 9 | |
| 10 'use strict'; | |
| 11 | |
| 12 /** @suppress {duplicate} */ | |
| 13 var remoting = remoting || {}; | |
| 14 | |
| 15 /** | |
| 16 * @type {boolean} Whether or not the last share was cancelled by the user. | |
| 17 * This controls what screen is shown when the host plugin signals | |
| 18 * completion. | |
| 19 * @private | |
| 20 */ | |
| 21 var lastShareWasCancelled_ = false; | |
| 22 | |
| 23 /** | |
| 24 * Start a host session. This is the main entry point for the host screen, | |
| 25 * called directly from the onclick action of a button on the home screen. | |
| 26 */ | |
| 27 remoting.tryShare = function() { | |
| 28 remoting.debug.log('Attempting to share...'); | |
| 29 lastShareWasCancelled_ = false; | |
| 30 if (remoting.oauth2.needsNewAccessToken()) { | |
| 31 remoting.debug.log('Refreshing token...'); | |
| 32 remoting.oauth2.refreshAccessToken(function() { | |
| 33 if (remoting.oauth2.needsNewAccessToken()) { | |
| 34 // If we still need it, we're going to infinite loop. | |
| 35 showShareError_(remoting.Error.AUTHENTICATION_FAILED); | |
| 36 throw 'Unable to get access token'; | |
| 37 } | |
| 38 remoting.tryShare(); | |
| 39 }); | |
| 40 return; | |
| 41 } | |
| 42 | |
| 43 onNatTraversalPolicyChanged_(true); // Hide warning by default. | |
| 44 remoting.setMode(remoting.AppMode.HOST_WAITING_FOR_CODE); | |
| 45 document.getElementById('cancel-button').disabled = false; | |
| 46 disableTimeoutCountdown_(); | |
| 47 | |
| 48 var div = document.getElementById('host-plugin-container'); | |
| 49 remoting.hostSession = new remoting.HostSession(); | |
| 50 remoting.hostSession.createPluginAndConnect( | |
| 51 document.getElementById('host-plugin-container'), | |
| 52 /** @type {string} */(remoting.oauth2.getCachedEmail()), | |
| 53 remoting.oauth2.getAccessToken(), | |
| 54 onNatTraversalPolicyChanged_, | |
| 55 onHostStateChanged_, | |
| 56 logDebugInfo_); | |
| 57 }; | |
| 58 | |
| 59 /** | |
| 60 * Callback for the host plugin to notify the web app of state changes. | |
| 61 * @param {remoting.HostSession.State} state The new state of the plugin. | |
| 62 */ | |
| 63 function onHostStateChanged_(state) { | |
| 64 if (state == remoting.HostSession.State.STARTING) { | |
| 65 // Nothing to do here. | |
| 66 remoting.debug.log('Host plugin state: STARTING'); | |
| 67 | |
| 68 } else if (state == remoting.HostSession.State.REQUESTED_ACCESS_CODE) { | |
| 69 // Nothing to do here. | |
| 70 remoting.debug.log('Host plugin state: REQUESTED_ACCESS_CODE'); | |
| 71 | |
| 72 } else if (state == remoting.HostSession.State.RECEIVED_ACCESS_CODE) { | |
| 73 remoting.debug.log('Host plugin state: RECEIVED_ACCESS_CODE'); | |
| 74 var accessCode = remoting.hostSession.getAccessCode(); | |
| 75 var accessCodeDisplay = document.getElementById('access-code-display'); | |
| 76 accessCodeDisplay.innerText = ''; | |
| 77 // Display the access code in groups of four digits for readability. | |
| 78 var kDigitsPerGroup = 4; | |
| 79 for (var i = 0; i < accessCode.length; i += kDigitsPerGroup) { | |
| 80 var nextFourDigits = document.createElement('span'); | |
| 81 nextFourDigits.className = 'access-code-digit-group'; | |
| 82 nextFourDigits.innerText = accessCode.substring(i, i + kDigitsPerGroup); | |
| 83 accessCodeDisplay.appendChild(nextFourDigits); | |
| 84 } | |
| 85 accessCodeExpiresIn_ = remoting.hostSession.getAccessCodeLifetime(); | |
| 86 if (accessCodeExpiresIn_ > 0) { // Check it hasn't expired. | |
| 87 accessCodeTimerId_ = setInterval( | |
| 88 'remoting.decrementAccessCodeTimeout_()', 1000); | |
| 89 timerRunning_ = true; | |
| 90 updateAccessCodeTimeoutElement_(); | |
| 91 updateTimeoutStyles_(); | |
| 92 remoting.setMode(remoting.AppMode.HOST_WAITING_FOR_CONNECTION); | |
| 93 } else { | |
| 94 // This can only happen if the cloud tells us that the code lifetime is | |
| 95 // <= 0s, which shouldn't happen so we don't care how clean this UX is. | |
| 96 remoting.debug.log('Access code already invalid on receipt!'); | |
| 97 remoting.cancelShare(); | |
| 98 } | |
| 99 | |
| 100 } else if (state == remoting.HostSession.State.CONNECTED) { | |
| 101 remoting.debug.log('Host plugin state: CONNECTED'); | |
| 102 var element = document.getElementById('host-shared-message'); | |
| 103 var client = remoting.hostSession.getClient(); | |
| 104 l10n.localizeElement(element, client); | |
| 105 remoting.setMode(remoting.AppMode.HOST_SHARED); | |
| 106 disableTimeoutCountdown_(); | |
| 107 | |
| 108 } else if (state == remoting.HostSession.State.DISCONNECTING) { | |
| 109 remoting.debug.log('Host plugin state: DISCONNECTING'); | |
| 110 | |
| 111 } else if (state == remoting.HostSession.State.DISCONNECTED) { | |
| 112 remoting.debug.log('Host plugin state: DISCONNECTED'); | |
| 113 if (remoting.currentMode != remoting.AppMode.HOST_SHARE_FAILED) { | |
| 114 // If an error is being displayed, then the plugin should not be able to | |
| 115 // hide it by setting the state. Errors must be dismissed by the user | |
| 116 // clicking OK, which puts the app into mode HOME. | |
| 117 if (lastShareWasCancelled_) { | |
| 118 remoting.setMode(remoting.AppMode.HOME); | |
| 119 } else { | |
| 120 remoting.setMode(remoting.AppMode.HOST_SHARE_FINISHED); | |
| 121 } | |
| 122 } | |
| 123 remoting.hostSession.removePlugin(); | |
| 124 | |
| 125 } else if (state == remoting.HostSession.State.ERROR) { | |
| 126 remoting.debug.log('Host plugin state: ERROR'); | |
| 127 showShareError_(remoting.Error.GENERIC); | |
| 128 } else { | |
| 129 remoting.debug.log('Unknown state -> ' + state); | |
| 130 } | |
| 131 } | |
| 132 | |
| 133 /** | |
| 134 * This is the callback that the host plugin invokes to indicate that there | |
| 135 * is additional debug log info to display. | |
| 136 * @param {string} msg The message (which will not be localized) to be logged. | |
| 137 */ | |
| 138 function logDebugInfo_(msg) { | |
| 139 remoting.debug.log('plugin: ' + msg); | |
| 140 } | |
| 141 | |
| 142 /** | |
| 143 * Show a host-side error message. | |
| 144 * | |
| 145 * @param {string} errorTag The error message to be localized and displayed. | |
| 146 * @return {void} Nothing. | |
| 147 */ | |
| 148 function showShareError_(errorTag) { | |
| 149 var errorDiv = document.getElementById('host-plugin-error'); | |
| 150 l10n.localizeElementFromTag(errorDiv, errorTag); | |
| 151 remoting.debug.log('Sharing error: ' + errorTag); | |
| 152 remoting.setMode(remoting.AppMode.HOST_SHARE_FAILED); | |
| 153 } | |
| 154 | |
| 155 /** | |
| 156 * Cancel an active or pending share operation. | |
| 157 * | |
| 158 * @return {void} Nothing. | |
| 159 */ | |
| 160 remoting.cancelShare = function() { | |
| 161 remoting.debug.log('Canceling share...'); | |
| 162 remoting.lastShareWasCancelled = true; | |
| 163 try { | |
| 164 remoting.hostSession.disconnect(); | |
| 165 } catch (error) { | |
| 166 // Hack to force JSCompiler type-safety. | |
| 167 var errorTyped = /** @type {{description: string}} */ error; | |
| 168 remoting.debug.log('Error disconnecting: ' + errorTyped.description + | |
| 169 '. The host plugin probably crashed.'); | |
| 170 // TODO(jamiewalch): Clean this up. We should have a class representing | |
| 171 // the host plugin, like we do for the client, which should handle crash | |
| 172 // reporting and it should use a more detailed error message than the | |
| 173 // default 'generic' one. See crbug.com/94624 | |
| 174 showShareError_(remoting.Error.GENERIC); | |
| 175 } | |
| 176 disableTimeoutCountdown_(); | |
| 177 }; | |
| 178 | |
| 179 /** | |
| 180 * @type {boolean} Whether or not the access code timeout countdown is running. | |
| 181 * @private | |
| 182 */ | |
| 183 var timerRunning_ = false; | |
| 184 | |
| 185 /** | |
| 186 * @type {number} The id of the access code expiry countdown timer. | |
| 187 * @private | |
| 188 */ | |
| 189 var accessCodeTimerId_ = 0; | |
| 190 | |
| 191 /** | |
| 192 * @type {number} The number of seconds until the access code expires. | |
| 193 * @private | |
| 194 */ | |
| 195 var accessCodeExpiresIn_ = 0; | |
| 196 | |
| 197 /** | |
| 198 * The timer callback function, which needs to be visible from the global | |
| 199 * namespace. | |
| 200 * @private | |
| 201 */ | |
| 202 remoting.decrementAccessCodeTimeout_ = function() { | |
| 203 --accessCodeExpiresIn_; | |
| 204 updateAccessCodeTimeoutElement_(); | |
| 205 }; | |
| 206 | |
| 207 /** | |
| 208 * Stop the access code timeout countdown if it is running. | |
| 209 */ | |
| 210 function disableTimeoutCountdown_() { | |
| 211 if (timerRunning_) { | |
| 212 clearInterval(accessCodeTimerId_); | |
| 213 timerRunning_ = false; | |
| 214 updateTimeoutStyles_(); | |
| 215 } | |
| 216 } | |
| 217 | |
| 218 /** | |
| 219 * Constants controlling the access code timer countdown display. | |
| 220 * @private | |
| 221 */ | |
| 222 var ACCESS_CODE_TIMER_DISPLAY_THRESHOLD_ = 30; | |
| 223 var ACCESS_CODE_RED_THRESHOLD_ = 10; | |
| 224 | |
| 225 /** | |
| 226 * Show/hide or restyle various elements, depending on the remaining countdown | |
| 227 * and timer state. | |
| 228 * | |
| 229 * @return {boolean} True if the timeout is in progress, false if it has | |
| 230 * expired. | |
| 231 */ | |
| 232 function updateTimeoutStyles_() { | |
| 233 if (timerRunning_) { | |
| 234 if (accessCodeExpiresIn_ <= 0) { | |
| 235 remoting.cancelShare(); | |
| 236 return false; | |
| 237 } | |
| 238 if (accessCodeExpiresIn_ <= ACCESS_CODE_RED_THRESHOLD_) { | |
| 239 addClass(document.getElementById('access-code-display'), 'expiring'); | |
| 240 } else { | |
| 241 removeClass(document.getElementById('access-code-display'), 'expiring'); | |
| 242 } | |
| 243 } | |
| 244 document.getElementById('access-code-countdown').hidden = | |
| 245 (accessCodeExpiresIn_ > ACCESS_CODE_TIMER_DISPLAY_THRESHOLD_) || | |
| 246 !timerRunning_; | |
| 247 return true; | |
| 248 } | |
| 249 | |
| 250 /** | |
| 251 * Update the text and appearance of the access code timeout element to | |
| 252 * reflect the time remaining. | |
| 253 */ | |
| 254 function updateAccessCodeTimeoutElement_() { | |
| 255 var pad = (accessCodeExpiresIn_ < 10) ? '0:0' : '0:'; | |
| 256 l10n.localizeElement(document.getElementById('seconds-remaining'), | |
| 257 pad + accessCodeExpiresIn_); | |
| 258 if (!updateTimeoutStyles_()) { | |
| 259 disableTimeoutCountdown_(); | |
| 260 } | |
| 261 } | |
| 262 | |
| 263 /** | |
| 264 * Callback to show or hide the NAT traversal warning when the policy changes. | |
| 265 * @param {boolean} enabled True if NAT traversal is enabled. | |
| 266 * @return {void} Nothing. | |
| 267 */ | |
| 268 function onNatTraversalPolicyChanged_(enabled) { | |
| 269 var container = document.getElementById('nat-box-container'); | |
| 270 container.hidden = enabled; | |
| 271 } | |
| OLD | NEW |