OLD | NEW |
| (Empty) |
1 // Copyright 2014 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 Display manager for WebUI OOBE and login. | |
7 */ | |
8 | |
9 // TODO(xiyuan): Find a better to share those constants. | |
10 /** @const */ var SCREEN_OOBE_NETWORK = 'connect'; | |
11 /** @const */ var SCREEN_OOBE_HID_DETECTION = 'hid-detection'; | |
12 /** @const */ var SCREEN_OOBE_EULA = 'eula'; | |
13 /** @const */ var SCREEN_OOBE_UPDATE = 'update'; | |
14 /** @const */ var SCREEN_OOBE_RESET = 'reset'; | |
15 /** @const */ var SCREEN_OOBE_ENROLLMENT = 'oauth-enrollment'; | |
16 /** @const */ var SCREEN_OOBE_KIOSK_ENABLE = 'kiosk-enable'; | |
17 /** @const */ var SCREEN_OOBE_AUTO_ENROLLMENT_CHECK = 'auto-enrollment-check'; | |
18 /** @const */ var SCREEN_GAIA_SIGNIN = 'gaia-signin'; | |
19 /** @const */ var SCREEN_ACCOUNT_PICKER = 'account-picker'; | |
20 /** @const */ var SCREEN_USER_IMAGE_PICKER = 'user-image'; | |
21 /** @const */ var SCREEN_ERROR_MESSAGE = 'error-message'; | |
22 /** @const */ var SCREEN_TPM_ERROR = 'tpm-error-message'; | |
23 /** @const */ var SCREEN_PASSWORD_CHANGED = 'password-changed'; | |
24 /** @const */ var SCREEN_CREATE_MANAGED_USER_FLOW = | |
25 'managed-user-creation'; | |
26 /** @const */ var SCREEN_APP_LAUNCH_SPLASH = 'app-launch-splash'; | |
27 /** @const */ var SCREEN_CONFIRM_PASSWORD = 'confirm-password'; | |
28 /** @const */ var SCREEN_FATAL_ERROR = 'fatal-error'; | |
29 /** @const */ var SCREEN_KIOSK_ENABLE = 'kiosk-enable'; | |
30 /** @const */ var SCREEN_TERMS_OF_SERVICE = 'terms-of-service'; | |
31 /** @const */ var SCREEN_WRONG_HWID = 'wrong-hwid'; | |
32 | |
33 /* Accelerator identifiers. Must be kept in sync with webui_login_view.cc. */ | |
34 /** @const */ var ACCELERATOR_CANCEL = 'cancel'; | |
35 /** @const */ var ACCELERATOR_ENROLLMENT = 'enrollment'; | |
36 /** @const */ var ACCELERATOR_KIOSK_ENABLE = 'kiosk_enable'; | |
37 /** @const */ var ACCELERATOR_VERSION = 'version'; | |
38 /** @const */ var ACCELERATOR_RESET = 'reset'; | |
39 /** @const */ var ACCELERATOR_FOCUS_PREV = 'focus_prev'; | |
40 /** @const */ var ACCELERATOR_FOCUS_NEXT = 'focus_next'; | |
41 /** @const */ var ACCELERATOR_DEVICE_REQUISITION = 'device_requisition'; | |
42 /** @const */ var ACCELERATOR_DEVICE_REQUISITION_REMORA = | |
43 'device_requisition_remora'; | |
44 /** @const */ var ACCELERATOR_DEVICE_REQUISITION_SHARK = | |
45 'device_requisition_shark'; | |
46 /** @const */ var ACCELERATOR_APP_LAUNCH_BAILOUT = 'app_launch_bailout'; | |
47 /** @const */ var ACCELERATOR_APP_LAUNCH_NETWORK_CONFIG = | |
48 'app_launch_network_config'; | |
49 | |
50 /* Signin UI state constants. Used to control header bar UI. */ | |
51 /** @const */ var SIGNIN_UI_STATE = { | |
52 HIDDEN: 0, | |
53 GAIA_SIGNIN: 1, | |
54 ACCOUNT_PICKER: 2, | |
55 WRONG_HWID_WARNING: 3, | |
56 MANAGED_USER_CREATION_FLOW: 4, | |
57 SAML_PASSWORD_CONFIRM: 5, | |
58 }; | |
59 | |
60 /* Possible UI states of the error screen. */ | |
61 /** @const */ var ERROR_SCREEN_UI_STATE = { | |
62 UNKNOWN: 'ui-state-unknown', | |
63 UPDATE: 'ui-state-update', | |
64 SIGNIN: 'ui-state-signin', | |
65 MANAGED_USER_CREATION_FLOW: 'ui-state-locally-managed', | |
66 KIOSK_MODE: 'ui-state-kiosk-mode', | |
67 LOCAL_STATE_ERROR: 'ui-state-local-state-error', | |
68 AUTO_ENROLLMENT_ERROR: 'ui-state-auto-enrollment-error', | |
69 ROLLBACK_ERROR: 'ui-state-rollback-error' | |
70 }; | |
71 | |
72 /* Possible types of UI. */ | |
73 /** @const */ var DISPLAY_TYPE = { | |
74 UNKNOWN: 'unknown', | |
75 OOBE: 'oobe', | |
76 LOGIN: 'login', | |
77 LOCK: 'lock', | |
78 USER_ADDING: 'user-adding', | |
79 APP_LAUNCH_SPLASH: 'app-launch-splash', | |
80 DESKTOP_USER_MANAGER: 'login-add-user' | |
81 }; | |
82 | |
83 cr.define('cr.ui.login', function() { | |
84 var Bubble = cr.ui.Bubble; | |
85 | |
86 /** | |
87 * Maximum time in milliseconds to wait for step transition to finish. | |
88 * The value is used as the duration for ensureTransitionEndEvent below. | |
89 * It needs to be inline with the step screen transition duration time | |
90 * defined in css file. The current value in css is 200ms. To avoid emulated | |
91 * webkitTransitionEnd fired before real one, 250ms is used. | |
92 * @const | |
93 */ | |
94 var MAX_SCREEN_TRANSITION_DURATION = 250; | |
95 | |
96 /** | |
97 * Groups of screens (screen IDs) that should have the same dimensions. | |
98 * @type Array.<Array.<string>> | |
99 * @const | |
100 */ | |
101 var SCREEN_GROUPS = [[SCREEN_OOBE_NETWORK, | |
102 SCREEN_OOBE_EULA, | |
103 SCREEN_OOBE_UPDATE, | |
104 SCREEN_OOBE_AUTO_ENROLLMENT_CHECK] | |
105 ]; | |
106 /** | |
107 * Group of screens (screen IDs) where factory-reset screen invocation is | |
108 * available. | |
109 * @type Array.<string> | |
110 * @const | |
111 */ | |
112 var RESET_AVAILABLE_SCREEN_GROUP = [ | |
113 SCREEN_OOBE_NETWORK, | |
114 SCREEN_OOBE_EULA, | |
115 SCREEN_OOBE_UPDATE, | |
116 SCREEN_OOBE_ENROLLMENT, | |
117 SCREEN_OOBE_AUTO_ENROLLMENT_CHECK, | |
118 SCREEN_GAIA_SIGNIN, | |
119 SCREEN_ACCOUNT_PICKER, | |
120 SCREEN_KIOSK_ENABLE, | |
121 SCREEN_ERROR_MESSAGE, | |
122 SCREEN_USER_IMAGE_PICKER, | |
123 SCREEN_TPM_ERROR, | |
124 SCREEN_PASSWORD_CHANGED, | |
125 SCREEN_TERMS_OF_SERVICE, | |
126 SCREEN_WRONG_HWID, | |
127 SCREEN_CONFIRM_PASSWORD, | |
128 SCREEN_FATAL_ERROR | |
129 ]; | |
130 | |
131 /** | |
132 * Group of screens (screen IDs) that are not participating in | |
133 * left-current-right animation. | |
134 * @type Array.<string> | |
135 * @const | |
136 */ | |
137 var NOT_ANIMATED_SCREEN_GROUP = [ | |
138 SCREEN_OOBE_RESET | |
139 ]; | |
140 | |
141 | |
142 /** | |
143 * OOBE screens group index. | |
144 */ | |
145 var SCREEN_GROUP_OOBE = 0; | |
146 | |
147 /** | |
148 * Constructor a display manager that manages initialization of screens, | |
149 * transitions, error messages display. | |
150 * | |
151 * @constructor | |
152 */ | |
153 function DisplayManager() { | |
154 } | |
155 | |
156 DisplayManager.prototype = { | |
157 /** | |
158 * Registered screens. | |
159 */ | |
160 screens_: [], | |
161 | |
162 /** | |
163 * Current OOBE step, index in the screens array. | |
164 * @type {number} | |
165 */ | |
166 currentStep_: 0, | |
167 | |
168 /** | |
169 * Whether version label can be toggled by ACCELERATOR_VERSION. | |
170 * @type {boolean} | |
171 */ | |
172 allowToggleVersion_: false, | |
173 | |
174 /** | |
175 * Whether keyboard navigation flow is enforced. | |
176 * @type {boolean} | |
177 */ | |
178 forceKeyboardFlow_: false, | |
179 | |
180 /** | |
181 * Whether virtual keyboard is shown. | |
182 * @type {boolean} | |
183 */ | |
184 virtualKeyboardShown_: false, | |
185 | |
186 /** | |
187 * Virtual keyboard width. | |
188 * @type {number} | |
189 */ | |
190 virtualKeyboardWidth_: 0, | |
191 | |
192 /** | |
193 * Virtual keyboard height. | |
194 * @type {number} | |
195 */ | |
196 virtualKeyboardHeight_: 0, | |
197 | |
198 /** | |
199 * Type of UI. | |
200 * @type {string} | |
201 */ | |
202 displayType_: DISPLAY_TYPE.UNKNOWN, | |
203 | |
204 /** | |
205 * Error message (bubble) was shown. This is checked in tests. | |
206 */ | |
207 errorMessageWasShownForTesting_: false, | |
208 | |
209 get displayType() { | |
210 return this.displayType_; | |
211 }, | |
212 | |
213 set displayType(displayType) { | |
214 this.displayType_ = displayType; | |
215 document.documentElement.setAttribute('screen', displayType); | |
216 }, | |
217 | |
218 get newKioskUI() { | |
219 return loadTimeData.getString('newKioskUI') == 'on'; | |
220 }, | |
221 | |
222 /** | |
223 * Returns dimensions of screen exluding header bar. | |
224 * @type {Object} | |
225 */ | |
226 get clientAreaSize() { | |
227 var container = $('outer-container'); | |
228 return {width: container.offsetWidth, height: container.offsetHeight}; | |
229 }, | |
230 | |
231 /** | |
232 * Gets current screen element. | |
233 * @type {HTMLElement} | |
234 */ | |
235 get currentScreen() { | |
236 return $(this.screens_[this.currentStep_]); | |
237 }, | |
238 | |
239 /** | |
240 * Hides/shows header (Shutdown/Add User/Cancel buttons). | |
241 * @param {boolean} hidden Whether header is hidden. | |
242 */ | |
243 get headerHidden() { | |
244 return $('login-header-bar').hidden; | |
245 }, | |
246 | |
247 set headerHidden(hidden) { | |
248 $('login-header-bar').hidden = hidden; | |
249 }, | |
250 | |
251 /** | |
252 * Virtual keyboard state (hidden/shown). | |
253 * @param {boolean} hidden Whether keyboard is shown. | |
254 */ | |
255 get virtualKeyboardShown() { | |
256 return this.virtualKeyboardShown_; | |
257 }, | |
258 | |
259 set virtualKeyboardShown(shown) { | |
260 this.virtualKeyboardShown_ = shown; | |
261 }, | |
262 | |
263 /** | |
264 * Sets the current size of the virtual keyboard. | |
265 * @param {number} width keyboard width | |
266 * @param {number} height keyboard height | |
267 */ | |
268 setVirtualKeyboardSize: function(width, height) { | |
269 this.virtualKeyboardWidth_ = width; | |
270 this.virtualKeyboardHeight_ = height; | |
271 }, | |
272 | |
273 /** | |
274 * Sets the current size of the client area (display size). | |
275 * @param {number} width client area width | |
276 * @param {number} height client area height | |
277 */ | |
278 setClientAreaSize: function(width, height) { | |
279 var clientArea = $('outer-container'); | |
280 var bottom = parseInt(window.getComputedStyle(clientArea).bottom); | |
281 clientArea.style.minHeight = cr.ui.toCssPx(height - bottom); | |
282 }, | |
283 | |
284 /** | |
285 * Toggles background of main body between transparency and solid. | |
286 * @param {boolean} solid Whether to show a solid background. | |
287 */ | |
288 set solidBackground(solid) { | |
289 if (solid) | |
290 document.body.classList.add('solid'); | |
291 else | |
292 document.body.classList.remove('solid'); | |
293 }, | |
294 | |
295 /** | |
296 * Forces keyboard based OOBE navigation. | |
297 * @param {boolean} value True if keyboard navigation flow is forced. | |
298 */ | |
299 set forceKeyboardFlow(value) { | |
300 this.forceKeyboardFlow_ = value; | |
301 if (value) { | |
302 keyboard.initializeKeyboardFlow(); | |
303 cr.ui.DropDown.enableKeyboardFlow(); | |
304 for (var i = 0; i < this.screens_.length; ++i) { | |
305 var screen = $(this.screens_[i]); | |
306 if (screen.enableKeyboardFlow) | |
307 screen.enableKeyboardFlow(); | |
308 } | |
309 } | |
310 }, | |
311 | |
312 /** | |
313 * Shows/hides version labels. | |
314 * @param {boolean} show Whether labels should be visible by default. If | |
315 * false, visibility can be toggled by ACCELERATOR_VERSION. | |
316 */ | |
317 showVersion: function(show) { | |
318 $('version-labels').hidden = !show; | |
319 this.allowToggleVersion_ = !show; | |
320 }, | |
321 | |
322 /** | |
323 * Handle accelerators. | |
324 * @param {string} name Accelerator name. | |
325 */ | |
326 handleAccelerator: function(name) { | |
327 var currentStepId = this.screens_[this.currentStep_]; | |
328 if (name == ACCELERATOR_CANCEL) { | |
329 if (this.currentScreen.cancel) { | |
330 this.currentScreen.cancel(); | |
331 } | |
332 } else if (name == ACCELERATOR_ENROLLMENT) { | |
333 if (currentStepId == SCREEN_GAIA_SIGNIN || | |
334 currentStepId == SCREEN_ACCOUNT_PICKER) { | |
335 chrome.send('toggleEnrollmentScreen'); | |
336 } else if (currentStepId == SCREEN_OOBE_NETWORK || | |
337 currentStepId == SCREEN_OOBE_EULA) { | |
338 // In this case update check will be skipped and OOBE will | |
339 // proceed straight to enrollment screen when EULA is accepted. | |
340 chrome.send('skipUpdateEnrollAfterEula'); | |
341 } else if (currentStepId == SCREEN_OOBE_ENROLLMENT) { | |
342 // This accelerator is also used to manually cancel auto-enrollment. | |
343 if (this.currentScreen.cancelAutoEnrollment) | |
344 this.currentScreen.cancelAutoEnrollment(); | |
345 } | |
346 } else if (name == ACCELERATOR_KIOSK_ENABLE) { | |
347 if (currentStepId == SCREEN_GAIA_SIGNIN || | |
348 currentStepId == SCREEN_ACCOUNT_PICKER) { | |
349 chrome.send('toggleKioskEnableScreen'); | |
350 } | |
351 } else if (name == ACCELERATOR_VERSION) { | |
352 if (this.allowToggleVersion_) | |
353 $('version-labels').hidden = !$('version-labels').hidden; | |
354 } else if (name == ACCELERATOR_RESET) { | |
355 if (RESET_AVAILABLE_SCREEN_GROUP.indexOf(currentStepId) != -1) | |
356 chrome.send('toggleResetScreen'); | |
357 } else if (name == ACCELERATOR_DEVICE_REQUISITION) { | |
358 if (this.isOobeUI()) | |
359 this.showDeviceRequisitionPrompt_(); | |
360 } else if (name == ACCELERATOR_DEVICE_REQUISITION_REMORA) { | |
361 if (this.isOobeUI()) | |
362 this.showDeviceRequisitionRemoraPrompt_( | |
363 'deviceRequisitionRemoraPromptText', 'remora'); | |
364 } else if (name == ACCELERATOR_DEVICE_REQUISITION_SHARK) { | |
365 if (this.isOobeUI()) | |
366 this.showDeviceRequisitionRemoraPrompt_( | |
367 'deviceRequisitionSharkPromptText', 'shark'); | |
368 } else if (name == ACCELERATOR_APP_LAUNCH_BAILOUT) { | |
369 if (currentStepId == SCREEN_APP_LAUNCH_SPLASH) | |
370 chrome.send('cancelAppLaunch'); | |
371 } else if (name == ACCELERATOR_APP_LAUNCH_NETWORK_CONFIG) { | |
372 if (currentStepId == SCREEN_APP_LAUNCH_SPLASH) | |
373 chrome.send('networkConfigRequest'); | |
374 } | |
375 | |
376 if (!this.forceKeyboardFlow_) | |
377 return; | |
378 | |
379 // Handle special accelerators for keyboard enhanced navigation flow. | |
380 if (name == ACCELERATOR_FOCUS_PREV) | |
381 keyboard.raiseKeyFocusPrevious(document.activeElement); | |
382 else if (name == ACCELERATOR_FOCUS_NEXT) | |
383 keyboard.raiseKeyFocusNext(document.activeElement); | |
384 }, | |
385 | |
386 /** | |
387 * Appends buttons to the button strip. | |
388 * @param {Array.<HTMLElement>} buttons Array with the buttons to append. | |
389 * @param {string} screenId Id of the screen that buttons belong to. | |
390 */ | |
391 appendButtons_: function(buttons, screenId) { | |
392 if (buttons) { | |
393 var buttonStrip = $(screenId + '-controls'); | |
394 if (buttonStrip) { | |
395 for (var i = 0; i < buttons.length; ++i) | |
396 buttonStrip.appendChild(buttons[i]); | |
397 } | |
398 } | |
399 }, | |
400 | |
401 /** | |
402 * Disables or enables control buttons on the specified screen. | |
403 * @param {HTMLElement} screen Screen which controls should be affected. | |
404 * @param {boolean} disabled Whether to disable controls. | |
405 */ | |
406 disableButtons_: function(screen, disabled) { | |
407 var buttons = document.querySelectorAll( | |
408 '#' + screen.id + '-controls button:not(.preserve-disabled-state)'); | |
409 for (var i = 0; i < buttons.length; ++i) { | |
410 buttons[i].disabled = disabled; | |
411 } | |
412 }, | |
413 | |
414 screenIsAnimated_: function(screenId) { | |
415 return NOT_ANIMATED_SCREEN_GROUP.indexOf(screenId) != -1; | |
416 }, | |
417 | |
418 /** | |
419 * Updates a step's css classes to reflect left, current, or right position. | |
420 * @param {number} stepIndex step index. | |
421 * @param {string} state one of 'left', 'current', 'right'. | |
422 */ | |
423 updateStep_: function(stepIndex, state) { | |
424 var stepId = this.screens_[stepIndex]; | |
425 var step = $(stepId); | |
426 var header = $('header-' + stepId); | |
427 var states = ['left', 'right', 'current']; | |
428 for (var i = 0; i < states.length; ++i) { | |
429 if (states[i] != state) { | |
430 step.classList.remove(states[i]); | |
431 header.classList.remove(states[i]); | |
432 } | |
433 } | |
434 | |
435 step.classList.add(state); | |
436 header.classList.add(state); | |
437 }, | |
438 | |
439 /** | |
440 * Switches to the next OOBE step. | |
441 * @param {number} nextStepIndex Index of the next step. | |
442 */ | |
443 toggleStep_: function(nextStepIndex, screenData) { | |
444 var currentStepId = this.screens_[this.currentStep_]; | |
445 var nextStepId = this.screens_[nextStepIndex]; | |
446 var oldStep = $(currentStepId); | |
447 var newStep = $(nextStepId); | |
448 var newHeader = $('header-' + nextStepId); | |
449 | |
450 // Disable controls before starting animation. | |
451 this.disableButtons_(oldStep, true); | |
452 | |
453 if (oldStep.onBeforeHide) | |
454 oldStep.onBeforeHide(); | |
455 | |
456 $('oobe').className = nextStepId; | |
457 | |
458 // Need to do this before calling newStep.onBeforeShow() so that new step | |
459 // is back in DOM tree and has correct offsetHeight / offsetWidth. | |
460 newStep.hidden = false; | |
461 | |
462 if (newStep.onBeforeShow) | |
463 newStep.onBeforeShow(screenData); | |
464 | |
465 newStep.classList.remove('hidden'); | |
466 | |
467 if (this.isOobeUI() && | |
468 this.screenIsAnimated_(nextStepId) && | |
469 this.screenIsAnimated_(currentStepId)) { | |
470 // Start gliding animation for OOBE steps. | |
471 if (nextStepIndex > this.currentStep_) { | |
472 for (var i = this.currentStep_; i < nextStepIndex; ++i) | |
473 this.updateStep_(i, 'left'); | |
474 this.updateStep_(nextStepIndex, 'current'); | |
475 } else if (nextStepIndex < this.currentStep_) { | |
476 for (var i = this.currentStep_; i > nextStepIndex; --i) | |
477 this.updateStep_(i, 'right'); | |
478 this.updateStep_(nextStepIndex, 'current'); | |
479 } | |
480 } else { | |
481 // Start fading animation for login display or reset screen. | |
482 oldStep.classList.add('faded'); | |
483 newStep.classList.remove('faded'); | |
484 if (!this.screenIsAnimated_(nextStepId)) { | |
485 newStep.classList.remove('left'); | |
486 newStep.classList.remove('right'); | |
487 } | |
488 } | |
489 | |
490 this.disableButtons_(newStep, false); | |
491 | |
492 // Adjust inner container height based on new step's height. | |
493 this.updateScreenSize(newStep); | |
494 | |
495 if (newStep.onAfterShow) | |
496 newStep.onAfterShow(screenData); | |
497 | |
498 // Workaround for gaia and network screens. | |
499 // Due to other origin iframe and long ChromeVox focusing correspondingly | |
500 // passive aria-label title is not pronounced. | |
501 // Gaia hack can be removed on fixed crbug.com/316726. | |
502 if (nextStepId == SCREEN_GAIA_SIGNIN) { | |
503 newStep.setAttribute( | |
504 'aria-label', | |
505 loadTimeData.getString('signinScreenTitle')); | |
506 } else if (nextStepId == SCREEN_OOBE_NETWORK) { | |
507 newStep.setAttribute( | |
508 'aria-label', | |
509 loadTimeData.getString('networkScreenAccessibleTitle')); | |
510 } | |
511 | |
512 // Default control to be focused (if specified). | |
513 var defaultControl = newStep.defaultControl; | |
514 | |
515 var outerContainer = $('outer-container'); | |
516 var innerContainer = $('inner-container'); | |
517 var isOOBE = this.isOobeUI(); | |
518 if (this.currentStep_ != nextStepIndex && | |
519 !oldStep.classList.contains('hidden')) { | |
520 if (oldStep.classList.contains('animated')) { | |
521 innerContainer.classList.add('animation'); | |
522 oldStep.addEventListener('webkitTransitionEnd', function f(e) { | |
523 oldStep.removeEventListener('webkitTransitionEnd', f); | |
524 if (oldStep.classList.contains('faded') || | |
525 oldStep.classList.contains('left') || | |
526 oldStep.classList.contains('right')) { | |
527 innerContainer.classList.remove('animation'); | |
528 oldStep.classList.add('hidden'); | |
529 if (!isOOBE) | |
530 oldStep.hidden = true; | |
531 } | |
532 // Refresh defaultControl. It could have changed. | |
533 var defaultControl = newStep.defaultControl; | |
534 if (defaultControl) | |
535 defaultControl.focus(); | |
536 }); | |
537 ensureTransitionEndEvent(oldStep, MAX_SCREEN_TRANSITION_DURATION); | |
538 } else { | |
539 oldStep.classList.add('hidden'); | |
540 oldStep.hidden = true; | |
541 if (defaultControl) | |
542 defaultControl.focus(); | |
543 } | |
544 } else { | |
545 // First screen on OOBE launch. | |
546 if (this.isOobeUI() && innerContainer.classList.contains('down')) { | |
547 innerContainer.classList.remove('down'); | |
548 innerContainer.addEventListener( | |
549 'webkitTransitionEnd', function f(e) { | |
550 innerContainer.removeEventListener('webkitTransitionEnd', f); | |
551 outerContainer.classList.remove('down'); | |
552 $('progress-dots').classList.remove('down'); | |
553 chrome.send('loginVisible', ['oobe']); | |
554 // Refresh defaultControl. It could have changed. | |
555 var defaultControl = newStep.defaultControl; | |
556 if (defaultControl) | |
557 defaultControl.focus(); | |
558 }); | |
559 ensureTransitionEndEvent(innerContainer, | |
560 MAX_SCREEN_TRANSITION_DURATION); | |
561 } else { | |
562 if (defaultControl) | |
563 defaultControl.focus(); | |
564 chrome.send('loginVisible', ['oobe']); | |
565 } | |
566 } | |
567 this.currentStep_ = nextStepIndex; | |
568 | |
569 $('step-logo').hidden = newStep.classList.contains('no-logo'); | |
570 | |
571 chrome.send('updateCurrentScreen', [this.currentScreen.id]); | |
572 }, | |
573 | |
574 /** | |
575 * Make sure that screen is initialized and decorated. | |
576 * @param {Object} screen Screen params dict, e.g. {id: screenId, data: {}}. | |
577 */ | |
578 preloadScreen: function(screen) { | |
579 var screenEl = $(screen.id); | |
580 if (screenEl.deferredDecorate !== undefined) { | |
581 screenEl.deferredDecorate(); | |
582 delete screenEl.deferredDecorate; | |
583 } | |
584 }, | |
585 | |
586 /** | |
587 * Show screen of given screen id. | |
588 * @param {Object} screen Screen params dict, e.g. {id: screenId, data: {}}. | |
589 */ | |
590 showScreen: function(screen) { | |
591 var screenId = screen.id; | |
592 | |
593 // Make sure the screen is decorated. | |
594 this.preloadScreen(screen); | |
595 | |
596 if (screen.data !== undefined && screen.data.disableAddUser) | |
597 DisplayManager.updateAddUserButtonStatus(true); | |
598 | |
599 | |
600 // Show sign-in screen instead of account picker if pod row is empty. | |
601 if (screenId == SCREEN_ACCOUNT_PICKER && $('pod-row').pods.length == 0) { | |
602 // Manually hide 'add-user' header bar, because of the case when | |
603 // 'Cancel' button is used on the offline login page. | |
604 $('add-user-header-bar-item').hidden = true; | |
605 Oobe.showSigninUI(true); | |
606 return; | |
607 } | |
608 | |
609 var data = screen.data; | |
610 var index = this.getScreenIndex_(screenId); | |
611 if (index >= 0) | |
612 this.toggleStep_(index, data); | |
613 }, | |
614 | |
615 /** | |
616 * Gets index of given screen id in screens_. | |
617 * @param {string} screenId Id of the screen to look up. | |
618 * @private | |
619 */ | |
620 getScreenIndex_: function(screenId) { | |
621 for (var i = 0; i < this.screens_.length; ++i) { | |
622 if (this.screens_[i] == screenId) | |
623 return i; | |
624 } | |
625 return -1; | |
626 }, | |
627 | |
628 /** | |
629 * Register an oobe screen. | |
630 * @param {Element} el Decorated screen element. | |
631 */ | |
632 registerScreen: function(el) { | |
633 var screenId = el.id; | |
634 this.screens_.push(screenId); | |
635 | |
636 var header = document.createElement('span'); | |
637 header.id = 'header-' + screenId; | |
638 header.textContent = el.header ? el.header : ''; | |
639 header.className = 'header-section'; | |
640 $('header-sections').appendChild(header); | |
641 | |
642 var dot = document.createElement('div'); | |
643 dot.id = screenId + '-dot'; | |
644 dot.className = 'progdot'; | |
645 var progressDots = $('progress-dots'); | |
646 if (progressDots) | |
647 progressDots.appendChild(dot); | |
648 | |
649 this.appendButtons_(el.buttons, screenId); | |
650 }, | |
651 | |
652 /** | |
653 * Updates inner container size based on the size of the current screen and | |
654 * other screens in the same group. | |
655 * Should be executed on screen change / screen size change. | |
656 * @param {!HTMLElement} screen Screen that is being shown. | |
657 */ | |
658 updateScreenSize: function(screen) { | |
659 // Have to reset any previously predefined screen size first | |
660 // so that screen contents would define it instead. | |
661 $('inner-container').style.height = ''; | |
662 $('inner-container').style.width = ''; | |
663 screen.style.width = ''; | |
664 screen.style.height = ''; | |
665 | |
666 $('outer-container').classList.toggle( | |
667 'fullscreen', screen.classList.contains('fullscreen')); | |
668 | |
669 var width = screen.getPreferredSize().width; | |
670 var height = screen.getPreferredSize().height; | |
671 for (var i = 0, screenGroup; screenGroup = SCREEN_GROUPS[i]; i++) { | |
672 if (screenGroup.indexOf(screen.id) != -1) { | |
673 // Set screen dimensions to maximum dimensions within this group. | |
674 for (var j = 0, screen2; screen2 = $(screenGroup[j]); j++) { | |
675 width = Math.max(width, screen2.getPreferredSize().width); | |
676 height = Math.max(height, screen2.getPreferredSize().height); | |
677 } | |
678 break; | |
679 } | |
680 } | |
681 $('inner-container').style.height = height + 'px'; | |
682 $('inner-container').style.width = width + 'px'; | |
683 // This requires |screen| to have 'box-sizing: border-box'. | |
684 screen.style.width = width + 'px'; | |
685 screen.style.height = height + 'px'; | |
686 }, | |
687 | |
688 /** | |
689 * Updates localized content of the screens like headers, buttons and links. | |
690 * Should be executed on language change. | |
691 */ | |
692 updateLocalizedContent_: function() { | |
693 for (var i = 0, screenId; screenId = this.screens_[i]; ++i) { | |
694 var screen = $(screenId); | |
695 var buttonStrip = $(screenId + '-controls'); | |
696 if (buttonStrip) | |
697 buttonStrip.innerHTML = ''; | |
698 // TODO(nkostylev): Update screen headers for new OOBE design. | |
699 this.appendButtons_(screen.buttons, screenId); | |
700 if (screen.updateLocalizedContent) | |
701 screen.updateLocalizedContent(); | |
702 } | |
703 | |
704 var currentScreenId = this.screens_[this.currentStep_]; | |
705 var currentScreen = $(currentScreenId); | |
706 this.updateScreenSize(currentScreen); | |
707 | |
708 // Trigger network drop-down to reload its state | |
709 // so that strings are reloaded. | |
710 // Will be reloaded if drowdown is actually shown. | |
711 cr.ui.DropDown.refresh(); | |
712 }, | |
713 | |
714 /** | |
715 * Initialized first group of OOBE screens. | |
716 */ | |
717 initializeOOBEScreens: function() { | |
718 if (this.isOobeUI() && $('inner-container').classList.contains('down')) { | |
719 for (var i = 0, screen; | |
720 screen = $(SCREEN_GROUPS[SCREEN_GROUP_OOBE][i]); i++) { | |
721 screen.hidden = false; | |
722 } | |
723 } | |
724 }, | |
725 | |
726 /** | |
727 * Prepares screens to use in login display. | |
728 */ | |
729 prepareForLoginDisplay_: function() { | |
730 for (var i = 0, screenId; screenId = this.screens_[i]; ++i) { | |
731 var screen = $(screenId); | |
732 screen.classList.add('faded'); | |
733 screen.classList.remove('right'); | |
734 screen.classList.remove('left'); | |
735 } | |
736 }, | |
737 | |
738 /** | |
739 * Shows the device requisition prompt. | |
740 */ | |
741 showDeviceRequisitionPrompt_: function() { | |
742 if (!this.deviceRequisitionDialog_) { | |
743 this.deviceRequisitionDialog_ = | |
744 new cr.ui.dialogs.PromptDialog(document.body); | |
745 this.deviceRequisitionDialog_.setOkLabel( | |
746 loadTimeData.getString('deviceRequisitionPromptOk')); | |
747 this.deviceRequisitionDialog_.setCancelLabel( | |
748 loadTimeData.getString('deviceRequisitionPromptCancel')); | |
749 } | |
750 this.deviceRequisitionDialog_.show( | |
751 loadTimeData.getString('deviceRequisitionPromptText'), | |
752 this.deviceRequisition_, | |
753 this.onConfirmDeviceRequisitionPrompt_.bind(this)); | |
754 }, | |
755 | |
756 /** | |
757 * Confirmation handle for the device requisition prompt. | |
758 * @param {string} value The value entered by the user. | |
759 * @private | |
760 */ | |
761 onConfirmDeviceRequisitionPrompt_: function(value) { | |
762 this.deviceRequisition_ = value; | |
763 chrome.send('setDeviceRequisition', [value == '' ? 'none' : value]); | |
764 }, | |
765 | |
766 /** | |
767 * Called when window size changed. Notifies current screen about change. | |
768 * @private | |
769 */ | |
770 onWindowResize_: function() { | |
771 var currentScreenId = this.screens_[this.currentStep_]; | |
772 var currentScreen = $(currentScreenId); | |
773 if (currentScreen) | |
774 currentScreen.onWindowResize(); | |
775 }, | |
776 | |
777 /* | |
778 * Updates the device requisition string shown in the requisition prompt. | |
779 * @param {string} requisition The device requisition. | |
780 */ | |
781 updateDeviceRequisition: function(requisition) { | |
782 this.deviceRequisition_ = requisition; | |
783 }, | |
784 | |
785 /** | |
786 * Shows the special remora/shark device requisition prompt. | |
787 * @private | |
788 */ | |
789 showDeviceRequisitionRemoraPrompt_: function(promptText, requisition) { | |
790 if (!this.deviceRequisitionRemoraDialog_) { | |
791 this.deviceRequisitionRemoraDialog_ = | |
792 new cr.ui.dialogs.ConfirmDialog(document.body); | |
793 this.deviceRequisitionRemoraDialog_.setOkLabel( | |
794 loadTimeData.getString('deviceRequisitionRemoraPromptOk')); | |
795 this.deviceRequisitionRemoraDialog_.setCancelLabel( | |
796 loadTimeData.getString('deviceRequisitionRemoraPromptCancel')); | |
797 } | |
798 this.deviceRequisitionRemoraDialog_.show( | |
799 loadTimeData.getString(promptText), | |
800 function() { // onShow | |
801 chrome.send('setDeviceRequisition', [requisition]); | |
802 }, | |
803 function() { // onCancel | |
804 chrome.send('setDeviceRequisition', ['none']); | |
805 }); | |
806 }, | |
807 | |
808 /** | |
809 * Returns true if Oobe UI is shown. | |
810 */ | |
811 isOobeUI: function() { | |
812 return document.body.classList.contains('oobe-display'); | |
813 }, | |
814 | |
815 /** | |
816 * Sets or unsets given |className| for top-level container. Useful for | |
817 * customizing #inner-container with CSS rules. All classes set with with | |
818 * this method will be removed after screen change. | |
819 * @param {string} className Class to toggle. | |
820 * @param {boolean} enabled Whether class should be enabled or disabled. | |
821 */ | |
822 toggleClass: function(className, enabled) { | |
823 $('oobe').classList.toggle(className, enabled); | |
824 } | |
825 }; | |
826 | |
827 /** | |
828 * Initializes display manager. | |
829 */ | |
830 DisplayManager.initialize = function() { | |
831 var givenDisplayType = DISPLAY_TYPE.UNKNOWN; | |
832 if (document.documentElement.hasAttribute('screen')) { | |
833 // Display type set in HTML property. | |
834 givenDisplayType = document.documentElement.getAttribute('screen'); | |
835 } else { | |
836 // Extracting display type from URL. | |
837 givenDisplayType = window.location.pathname.substr(1); | |
838 } | |
839 var instance = Oobe.getInstance(); | |
840 Object.getOwnPropertyNames(DISPLAY_TYPE).forEach(function(type) { | |
841 if (DISPLAY_TYPE[type] == givenDisplayType) { | |
842 instance.displayType = givenDisplayType; | |
843 } | |
844 }); | |
845 if (instance.displayType == DISPLAY_TYPE.UNKNOWN) { | |
846 console.error("Unknown display type '" + givenDisplayType + | |
847 "'. Setting default."); | |
848 instance.displayType = DISPLAY_TYPE.LOGIN; | |
849 } | |
850 | |
851 instance.initializeOOBEScreens(); | |
852 | |
853 window.addEventListener('resize', instance.onWindowResize_.bind(instance)); | |
854 }; | |
855 | |
856 /** | |
857 * Returns offset (top, left) of the element. | |
858 * @param {!Element} element HTML element. | |
859 * @return {!Object} The offset (top, left). | |
860 */ | |
861 DisplayManager.getOffset = function(element) { | |
862 var x = 0; | |
863 var y = 0; | |
864 while (element && !isNaN(element.offsetLeft) && !isNaN(element.offsetTop)) { | |
865 x += element.offsetLeft - element.scrollLeft; | |
866 y += element.offsetTop - element.scrollTop; | |
867 element = element.offsetParent; | |
868 } | |
869 return { top: y, left: x }; | |
870 }; | |
871 | |
872 /** | |
873 * Returns position (top, left, right, bottom) of the element. | |
874 * @param {!Element} element HTML element. | |
875 * @return {!Object} Element position (top, left, right, bottom). | |
876 */ | |
877 DisplayManager.getPosition = function(element) { | |
878 var offset = DisplayManager.getOffset(element); | |
879 return { top: offset.top, | |
880 right: window.innerWidth - element.offsetWidth - offset.left, | |
881 bottom: window.innerHeight - element.offsetHeight - offset.top, | |
882 left: offset.left }; | |
883 }; | |
884 | |
885 /** | |
886 * Disables signin UI. | |
887 */ | |
888 DisplayManager.disableSigninUI = function() { | |
889 $('login-header-bar').disabled = true; | |
890 $('pod-row').disabled = true; | |
891 }; | |
892 | |
893 /** | |
894 * Shows signin UI. | |
895 * @param {string} opt_email An optional email for signin UI. | |
896 */ | |
897 DisplayManager.showSigninUI = function(opt_email) { | |
898 var currentScreenId = Oobe.getInstance().currentScreen.id; | |
899 if (currentScreenId == SCREEN_GAIA_SIGNIN) | |
900 $('login-header-bar').signinUIState = SIGNIN_UI_STATE.GAIA_SIGNIN; | |
901 else if (currentScreenId == SCREEN_ACCOUNT_PICKER) | |
902 $('login-header-bar').signinUIState = SIGNIN_UI_STATE.ACCOUNT_PICKER; | |
903 chrome.send('showAddUser', [opt_email]); | |
904 }; | |
905 | |
906 /** | |
907 * Resets sign-in input fields. | |
908 * @param {boolean} forceOnline Whether online sign-in should be forced. | |
909 * If |forceOnline| is false previously used sign-in type will be used. | |
910 */ | |
911 DisplayManager.resetSigninUI = function(forceOnline) { | |
912 var currentScreenId = Oobe.getInstance().currentScreen.id; | |
913 | |
914 $(SCREEN_GAIA_SIGNIN).reset( | |
915 currentScreenId == SCREEN_GAIA_SIGNIN, forceOnline); | |
916 $('login-header-bar').disabled = false; | |
917 $('pod-row').reset(currentScreenId == SCREEN_ACCOUNT_PICKER); | |
918 }; | |
919 | |
920 /** | |
921 * Shows sign-in error bubble. | |
922 * @param {number} loginAttempts Number of login attemps tried. | |
923 * @param {string} message Error message to show. | |
924 * @param {string} link Text to use for help link. | |
925 * @param {number} helpId Help topic Id associated with help link. | |
926 */ | |
927 DisplayManager.showSignInError = function(loginAttempts, message, link, | |
928 helpId) { | |
929 var error = document.createElement('div'); | |
930 | |
931 var messageDiv = document.createElement('div'); | |
932 messageDiv.className = 'error-message-bubble'; | |
933 messageDiv.textContent = message; | |
934 error.appendChild(messageDiv); | |
935 | |
936 if (link) { | |
937 messageDiv.classList.add('error-message-bubble-padding'); | |
938 | |
939 var helpLink = document.createElement('a'); | |
940 helpLink.href = '#'; | |
941 helpLink.textContent = link; | |
942 helpLink.addEventListener('click', function(e) { | |
943 chrome.send('launchHelpApp', [helpId]); | |
944 e.preventDefault(); | |
945 }); | |
946 error.appendChild(helpLink); | |
947 } | |
948 | |
949 var currentScreen = Oobe.getInstance().currentScreen; | |
950 if (currentScreen && typeof currentScreen.showErrorBubble === 'function') { | |
951 currentScreen.showErrorBubble(loginAttempts, error); | |
952 this.errorMessageWasShownForTesting_ = true; | |
953 } | |
954 }; | |
955 | |
956 /** | |
957 * Shows password changed screen that offers migration. | |
958 * @param {boolean} showError Whether to show the incorrect password error. | |
959 */ | |
960 DisplayManager.showPasswordChangedScreen = function(showError) { | |
961 login.PasswordChangedScreen.show(showError); | |
962 }; | |
963 | |
964 /** | |
965 * Shows dialog to create managed user. | |
966 */ | |
967 DisplayManager.showManagedUserCreationScreen = function() { | |
968 login.ManagedUserCreationScreen.show(); | |
969 }; | |
970 | |
971 /** | |
972 * Shows TPM error screen. | |
973 */ | |
974 DisplayManager.showTpmError = function() { | |
975 login.TPMErrorMessageScreen.show(); | |
976 }; | |
977 | |
978 /** | |
979 * Clears error bubble. | |
980 */ | |
981 DisplayManager.clearErrors = function() { | |
982 $('bubble').hide(); | |
983 this.errorMessageWasShownForTesting_ = false; | |
984 | |
985 var bubbles = document.querySelectorAll('.bubble-shown'); | |
986 for (var i = 0; i < bubbles.length; ++i) | |
987 bubbles[i].classList.remove('bubble-shown'); | |
988 }; | |
989 | |
990 /** | |
991 * Sets text content for a div with |labelId|. | |
992 * @param {string} labelId Id of the label div. | |
993 * @param {string} labelText Text for the label. | |
994 */ | |
995 DisplayManager.setLabelText = function(labelId, labelText) { | |
996 $(labelId).textContent = labelText; | |
997 }; | |
998 | |
999 /** | |
1000 * Sets the text content of the enterprise info message. | |
1001 * @param {string} messageText The message text. | |
1002 */ | |
1003 DisplayManager.setEnterpriseInfo = function(messageText) { | |
1004 $('enterprise-info-message').textContent = messageText; | |
1005 if (messageText) { | |
1006 $('enterprise-info').hidden = false; | |
1007 } | |
1008 }; | |
1009 | |
1010 /** | |
1011 * Disable Add users button if said. | |
1012 * @param {boolean} disable true to disable | |
1013 */ | |
1014 DisplayManager.updateAddUserButtonStatus = function(disable) { | |
1015 $('add-user-button').disabled = disable; | |
1016 $('add-user-button').classList[ | |
1017 disable ? 'add' : 'remove']('button-restricted'); | |
1018 $('add-user-button').title = disable ? | |
1019 loadTimeData.getString('disabledAddUserTooltip') : ''; | |
1020 } | |
1021 | |
1022 /** | |
1023 * Clears password field in user-pod. | |
1024 */ | |
1025 DisplayManager.clearUserPodPassword = function() { | |
1026 $('pod-row').clearFocusedPod(); | |
1027 }; | |
1028 | |
1029 /** | |
1030 * Restores input focus to currently selected pod. | |
1031 */ | |
1032 DisplayManager.refocusCurrentPod = function() { | |
1033 $('pod-row').refocusCurrentPod(); | |
1034 }; | |
1035 | |
1036 // Export | |
1037 return { | |
1038 DisplayManager: DisplayManager | |
1039 }; | |
1040 }); | |
OLD | NEW |