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.
7 /** @suppress {duplicate} */
8 var remoting
= remoting
|| {};
11 * @param {Array.<remoting.HostSetupFlow.State>} sequence Sequence of
15 remoting
.HostSetupFlow = function(sequence
) {
16 this.sequence_
= sequence
;
17 this.currentStep_
= 0;
18 this.state_
= sequence
[0];
24 remoting
.HostSetupFlow
.State
= {
30 // Used on Mac OS X to prompt the user to manually install a .dmg package.
44 REGISTRATION_FAILED
: 9,
45 START_HOST_FAILED
: 10,
46 UPDATE_PIN_FAILED
: 11,
50 /** @return {remoting.HostSetupFlow.State} Current state of the flow. */
51 remoting
.HostSetupFlow
.prototype.getState = function() {
56 * @param {remoting.HostController.AsyncResult} result Result of the
58 * @return {remoting.HostSetupFlow.State} New state.
60 remoting
.HostSetupFlow
.prototype.switchToNextStep = function(result
) {
61 if (this.state_
== remoting
.HostSetupFlow
.State
.NONE
) {
64 if (result
== remoting
.HostController
.AsyncResult
.OK
) {
65 // If the current step was successful then switch to the next
66 // step in the sequence.
67 if (this.currentStep_
< this.sequence_
.length
- 1) {
68 this.currentStep_
+= 1;
69 this.state_
= this.sequence_
[this.currentStep_
];
71 this.state_
= remoting
.HostSetupFlow
.State
.NONE
;
73 } else if (result
== remoting
.HostController
.AsyncResult
.CANCELLED
) {
74 // Stop the setup flow if user rejected one of the actions.
75 this.state_
= remoting
.HostSetupFlow
.State
.NONE
;
77 // Current step failed, so switch to corresponding error state.
78 if (this.state_
== remoting
.HostSetupFlow
.State
.STARTING_HOST
) {
79 if (result
== remoting
.HostController
.AsyncResult
.FAILED_DIRECTORY
) {
80 this.state_
= remoting
.HostSetupFlow
.State
.REGISTRATION_FAILED
;
82 this.state_
= remoting
.HostSetupFlow
.State
.START_HOST_FAILED
;
84 } else if (this.state_
== remoting
.HostSetupFlow
.State
.UPDATING_PIN
) {
85 this.state_
= remoting
.HostSetupFlow
.State
.UPDATE_PIN_FAILED
;
86 } else if (this.state_
== remoting
.HostSetupFlow
.State
.STOPPING_HOST
) {
87 this.state_
= remoting
.HostSetupFlow
.State
.STOP_HOST_FAILED
;
89 // TODO(sergeyu): Add other error states and use them here.
90 this.state_
= remoting
.HostSetupFlow
.State
.START_HOST_FAILED
;
97 * @param {remoting.HostController} hostController The HostController
98 * responsible for the host daemon.
101 remoting
.HostSetupDialog = function(hostController
) {
102 this.hostController_
= hostController
;
103 this.pinEntry_
= document
.getElementById('daemon-pin-entry');
104 this.pinConfirm_
= document
.getElementById('daemon-pin-confirm');
105 this.pinErrorDiv_
= document
.getElementById('daemon-pin-error-div');
106 this.pinErrorMessage_
= document
.getElementById('daemon-pin-error-message');
108 /** @type {remoting.HostSetupFlow} */
109 this.flow_
= new remoting
.HostSetupFlow([remoting
.HostSetupFlow
.State
.NONE
]);
111 /** @type {remoting.HostSetupDialog} */
113 /** @param {Event} event The event. */
114 var onPinSubmit = function(event
) {
115 event
.preventDefault();
118 var form
= document
.getElementById('ask-pin-form');
119 form
.addEventListener('submit', onPinSubmit
, false);
120 /** @param {Event} event The event. */
121 var onDaemonPinEntryKeyPress = function(event
) {
122 if (event
.which
== 13) {
123 document
.getElementById('daemon-pin-confirm').focus();
124 event
.preventDefault();
127 /** @param {Event} event A keypress event. */
128 var noDigitsInPin = function(event
) {
129 if (event
.which
== 13) {
130 return; // Otherwise the "submit" action can't be triggered by Enter.
132 if ((event
.which
>= 48) && (event
.which
<= 57)) {
135 event
.preventDefault();
137 this.pinEntry_
.addEventListener('keypress', onDaemonPinEntryKeyPress
, false);
138 this.pinEntry_
.addEventListener('keypress', noDigitsInPin
, false);
139 this.pinConfirm_
.addEventListener('keypress', noDigitsInPin
, false);
141 this.usageStats_
= document
.getElementById('usagestats-consent');
142 this.usageStatsCheckbox_
=
143 document
.getElementById('usagestats-consent-checkbox');
147 * Show the dialog in order to get a PIN prior to starting the daemon. When the
148 * user clicks OK, the dialog shows a spinner until the daemon has started.
150 * @return {void} Nothing.
152 remoting
.HostSetupDialog
.prototype.showForStart = function() {
153 // Although we don't need an access token in order to start the host,
154 // using callWithToken here ensures consistent error handling in the
155 // case where the refresh token is invalid.
156 remoting
.oauth2
.callWithToken(this.showForStartWithToken_
.bind(this),
157 remoting
.showErrorMessage
);
161 * @param {string} token The OAuth2 token.
164 remoting
.HostSetupDialog
.prototype.showForStartWithToken_ = function(token
) {
165 /** @type {remoting.HostSetupDialog} */
169 * @param {boolean} supported True if crash dump reporting is supported by
171 * @param {boolean} allowed True if crash dump reporting is allowed.
172 * @param {boolean} set_by_policy True if crash dump reporting is controlled
175 var onGetConsent = function(supported
, allowed
, set_by_policy
) {
176 that
.usageStats_
.hidden
= !supported
;
177 that
.usageStatsCheckbox_
.checked
= allowed
;
178 that
.usageStatsCheckbox_
.disabled
= set_by_policy
;
180 this.usageStats_
.hidden
= false;
181 this.usageStatsCheckbox_
.checked
= true;
182 this.hostController_
.getConsent(onGetConsent
);
185 remoting
.HostSetupFlow
.State
.ASK_PIN
,
186 remoting
.HostSetupFlow
.State
.STARTING_HOST
,
187 remoting
.HostSetupFlow
.State
.HOST_STARTED
];
189 if (navigator
.platform
.indexOf('Mac') != -1 &&
190 this.hostController_
.state() ==
191 remoting
.HostController
.State
.NOT_INSTALLED
) {
192 flow
.unshift(remoting
.HostSetupFlow
.State
.INSTALL_HOST
);
195 this.startNewFlow_(flow
);
199 * Show the dialog in order to change the PIN associated with a running daemon.
201 * @return {void} Nothing.
203 remoting
.HostSetupDialog
.prototype.showForPin = function() {
204 this.usageStats_
.hidden
= true;
206 [remoting
.HostSetupFlow
.State
.ASK_PIN
,
207 remoting
.HostSetupFlow
.State
.UPDATING_PIN
,
208 remoting
.HostSetupFlow
.State
.UPDATED_PIN
]);
212 * Show the dialog in order to stop the daemon.
214 * @return {void} Nothing.
216 remoting
.HostSetupDialog
.prototype.showForStop = function() {
217 // TODO(sergeyu): Add another step to unregister the host, crubg.com/121146 .
219 [remoting
.HostSetupFlow
.State
.STOPPING_HOST
,
220 remoting
.HostSetupFlow
.State
.HOST_STOPPED
]);
224 * @return {void} Nothing.
226 remoting
.HostSetupDialog
.prototype.hide = function() {
227 remoting
.setMode(remoting
.AppMode
.HOME
);
231 * Starts new flow with the specified sequence of steps.
232 * @param {Array.<remoting.HostSetupFlow.State>} sequence Sequence of steps.
235 remoting
.HostSetupDialog
.prototype.startNewFlow_ = function(sequence
) {
236 this.flow_
= new remoting
.HostSetupFlow(sequence
);
237 this.pinEntry_
.value
= '';
238 this.pinConfirm_
.value
= '';
239 this.pinErrorDiv_
.hidden
= true;
244 * Updates current UI mode according to the current state of the setup
245 * flow and start the action corresponding to the current step (if
249 remoting
.HostSetupDialog
.prototype.updateState_ = function() {
250 remoting
.hostController
.updateDom();
252 /** @param {string} tag */
253 function showProcessingMessage(tag
) {
254 var messageDiv
= document
.getElementById('host-setup-processing-message');
255 l10n
.localizeElementFromTag(messageDiv
, tag
);
256 remoting
.setMode(remoting
.AppMode
.HOST_SETUP_PROCESSING
);
258 /** @param {string} tag1
259 * @param {string=} opt_tag2 */
260 function showDoneMessage(tag1
, opt_tag2
) {
261 var messageDiv
= document
.getElementById('host-setup-done-message');
262 l10n
.localizeElementFromTag(messageDiv
, tag1
);
263 messageDiv
= document
.getElementById('host-setup-done-message-2');
265 l10n
.localizeElementFromTag(messageDiv
, opt_tag2
);
267 messageDiv
.innerText
= '';
269 remoting
.setMode(remoting
.AppMode
.HOST_SETUP_DONE
);
271 /** @param {string} tag */
272 function showErrorMessage(tag
) {
273 var errorDiv
= document
.getElementById('host-setup-error-message');
274 l10n
.localizeElementFromTag(errorDiv
, tag
);
275 remoting
.setMode(remoting
.AppMode
.HOST_SETUP_ERROR
);
278 var state
= this.flow_
.getState();
279 if (state
== remoting
.HostSetupFlow
.State
.NONE
) {
281 } else if (state
== remoting
.HostSetupFlow
.State
.ASK_PIN
) {
282 remoting
.setMode(remoting
.AppMode
.HOST_SETUP_ASK_PIN
);
283 } else if (state
== remoting
.HostSetupFlow
.State
.INSTALL_HOST
) {
284 remoting
.setMode(remoting
.AppMode
.HOST_SETUP_INSTALL
);
286 'https://dl.google.com/chrome-remote-desktop/chromeremotedesktop.dmg';
287 } else if (state
== remoting
.HostSetupFlow
.State
.STARTING_HOST
) {
288 showProcessingMessage(/*i18n-content*/'HOST_SETUP_STARTING');
290 } else if (state
== remoting
.HostSetupFlow
.State
.UPDATING_PIN
) {
291 showProcessingMessage(/*i18n-content*/'HOST_SETUP_UPDATING_PIN');
293 } else if (state
== remoting
.HostSetupFlow
.State
.STOPPING_HOST
) {
294 showProcessingMessage(/*i18n-content*/'HOST_SETUP_STOPPING');
296 } else if (state
== remoting
.HostSetupFlow
.State
.HOST_STARTED
) {
297 // TODO(jamiewalch): Only display the second string if the computer's power
298 // management settings indicate that it's necessary.
299 showDoneMessage(/*i18n-content*/'HOST_SETUP_STARTED',
300 /*i18n-content*/'HOST_SETUP_STARTED_DISABLE_SLEEP');
301 } else if (state
== remoting
.HostSetupFlow
.State
.UPDATED_PIN
) {
302 showDoneMessage(/*i18n-content*/'HOST_SETUP_UPDATED_PIN');
303 } else if (state
== remoting
.HostSetupFlow
.State
.HOST_STOPPED
) {
304 showDoneMessage(/*i18n-content*/'HOST_SETUP_STOPPED');
305 } else if (state
== remoting
.HostSetupFlow
.State
.REGISTRATION_FAILED
) {
306 showErrorMessage(/*i18n-content*/'HOST_SETUP_REGISTRATION_FAILED');
307 } else if (state
== remoting
.HostSetupFlow
.State
.START_HOST_FAILED
) {
308 showErrorMessage(/*i18n-content*/'HOST_SETUP_HOST_FAILED');
309 } else if (state
== remoting
.HostSetupFlow
.State
.UPDATE_PIN_FAILED
) {
310 showErrorMessage(/*i18n-content*/'HOST_SETUP_UPDATE_PIN_FAILED');
311 } else if (state
== remoting
.HostSetupFlow
.State
.STOP_HOST_FAILED
) {
312 showErrorMessage(/*i18n-content*/'HOST_SETUP_STOP_FAILED');
317 * Registers and starts the host.
319 remoting
.HostSetupDialog
.prototype.startHost_ = function() {
320 /** @type {remoting.HostSetupDialog} */
322 /** @type {remoting.HostSetupFlow} */
323 var flow
= this.flow_
;
325 /** @param {remoting.HostController.AsyncResult} result */
326 function onHostStarted(result
) {
327 if (flow
!== that
.flow_
||
328 flow
.getState() != remoting
.HostSetupFlow
.State
.STARTING_HOST
) {
329 console
.error('Host setup was interrupted when starting the host');
333 flow
.switchToNextStep(result
);
336 this.hostController_
.start(this.flow_
.pin
, this.flow_
.consent
, onHostStarted
);
339 remoting
.HostSetupDialog
.prototype.updatePin_ = function() {
340 /** @type {remoting.HostSetupDialog} */
342 /** @type {remoting.HostSetupFlow} */
343 var flow
= this.flow_
;
345 /** @param {remoting.HostController.AsyncResult} result */
346 function onPinUpdated(result
) {
347 if (flow
!== that
.flow_
||
348 flow
.getState() != remoting
.HostSetupFlow
.State
.UPDATING_PIN
) {
349 console
.error('Host setup was interrupted when updating PIN');
353 flow
.switchToNextStep(result
);
357 this.hostController_
.updatePin(flow
.pin
, onPinUpdated
);
363 remoting
.HostSetupDialog
.prototype.stopHost_ = function() {
364 /** @type {remoting.HostSetupDialog} */
366 /** @type {remoting.HostSetupFlow} */
367 var flow
= this.flow_
;
369 /** @param {remoting.HostController.AsyncResult} result */
370 function onHostStopped(result
) {
371 if (flow
!== that
.flow_
||
372 flow
.getState() != remoting
.HostSetupFlow
.State
.STOPPING_HOST
) {
373 console
.error('Host setup was interrupted when stopping the host');
377 flow
.switchToNextStep(result
);
380 this.hostController_
.stop(onHostStopped
);
384 remoting
.HostSetupDialog
.prototype.onPinSubmit_ = function() {
385 if (this.flow_
.getState() != remoting
.HostSetupFlow
.State
.ASK_PIN
) {
386 console
.error('PIN submitted in an invalid state', this.flow_
.getState());
389 var pin1
= this.pinEntry_
.value
;
390 var pin2
= this.pinConfirm_
.value
;
392 l10n
.localizeElementFromTag(
393 this.pinErrorMessage_
, /*i18n-content*/'PINS_NOT_EQUAL');
394 this.pinErrorDiv_
.hidden
= false;
395 this.prepareForPinEntry_();
398 if (!remoting
.HostSetupDialog
.validPin_(pin1
)) {
399 l10n
.localizeElementFromTag(
400 this.pinErrorMessage_
, /*i18n-content*/'INVALID_PIN');
401 this.pinErrorDiv_
.hidden
= false;
402 this.prepareForPinEntry_();
405 this.pinErrorDiv_
.hidden
= true;
406 this.flow_
.pin
= pin1
;
407 this.flow_
.consent
= !this.usageStats_
.hidden
&&
408 (this.usageStatsCheckbox_
.value
== "on");
409 this.flow_
.switchToNextStep(remoting
.HostController
.AsyncResult
.OK
);
414 remoting
.HostSetupDialog
.prototype.prepareForPinEntry_ = function() {
415 this.pinEntry_
.value
= '';
416 this.pinConfirm_
.value
= '';
417 this.pinEntry_
.focus();
421 * Returns whether a PIN is valid.
424 * @param {string} pin A PIN.
425 * @return {boolean} Whether the PIN is valid.
427 remoting
.HostSetupDialog
.validPin_ = function(pin
) {
428 if (pin
.length
< 6) {
431 for (var i
= 0; i
< pin
.length
; i
++) {
432 var c
= pin
.charAt(i
);
433 if ((c
< '0') || (c
> '9')) {
441 * @return {void} Nothing.
443 remoting
.HostSetupDialog
.prototype.onInstallDialogOk = function() {
444 var state
= this.hostController_
.state();
445 if (state
== remoting
.HostController
.State
.STOPPED
) {
446 this.flow_
.switchToNextStep(remoting
.HostController
.AsyncResult
.OK
);
449 remoting
.setMode(remoting
.AppMode
.HOST_SETUP_INSTALL_PENDING
);
454 * @return {void} Nothing.
456 remoting
.HostSetupDialog
.prototype.onInstallDialogRetry = function() {
457 remoting
.setMode(remoting
.AppMode
.HOST_SETUP_INSTALL
);
460 /** @type {remoting.HostSetupDialog} */
461 remoting
.hostSetupDialog
= null;