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