Mac: Fix performance issues with remote CoreAnimation
[chromium-blink-merge.git] / ash / wm / lock_state_controller.cc
blob7ba8a2f20723b6075b0645c736e9cd004e4fc865
1 // Copyright 2013 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.
5 #include "ash/wm/lock_state_controller.h"
7 #include <algorithm>
8 #include <string>
10 #include "ash/accessibility_delegate.h"
11 #include "ash/ash_switches.h"
12 #include "ash/cancel_mode.h"
13 #include "ash/metrics/user_metrics_recorder.h"
14 #include "ash/shell.h"
15 #include "ash/shell_delegate.h"
16 #include "ash/shell_window_ids.h"
17 #include "ash/wm/session_state_animator.h"
18 #include "ash/wm/session_state_animator_impl.h"
19 #include "base/bind_helpers.h"
20 #include "base/command_line.h"
21 #include "base/strings/string_util.h"
22 #include "base/timer/timer.h"
23 #include "ui/aura/window_tree_host.h"
24 #include "ui/views/controls/menu/menu_controller.h"
25 #include "ui/wm/core/compound_event_filter.h"
27 #if defined(OS_CHROMEOS)
28 #include "base/sys_info.h"
29 #include "media/audio/sounds/sounds_manager.h"
30 #endif
32 #if defined(OS_CHROMEOS)
33 using media::SoundsManager;
34 #endif
36 namespace ash {
38 namespace {
40 #if defined(OS_CHROMEOS)
41 const int kMaxShutdownSoundDurationMs = 1500;
42 #endif
44 } // namespace
46 const int LockStateController::kLockTimeoutMs = 400;
47 const int LockStateController::kShutdownTimeoutMs = 400;
48 const int LockStateController::kLockFailTimeoutMs = 8000;
49 const int LockStateController::kLockToShutdownTimeoutMs = 150;
50 const int LockStateController::kShutdownRequestDelayMs = 50;
52 LockStateController::TestApi::TestApi(LockStateController* controller)
53 : controller_(controller) {
56 LockStateController::TestApi::~TestApi() {
59 LockStateController::LockStateController()
60 : animator_(new SessionStateAnimatorImpl()),
61 login_status_(user::LOGGED_IN_NONE),
62 system_is_locked_(false),
63 shutting_down_(false),
64 shutdown_after_lock_(false),
65 animating_lock_(false),
66 can_cancel_lock_animation_(false),
67 weak_ptr_factory_(this) {
68 Shell::GetPrimaryRootWindow()->GetHost()->AddObserver(this);
71 LockStateController::~LockStateController() {
72 Shell::GetPrimaryRootWindow()->GetHost()->RemoveObserver(this);
75 void LockStateController::SetDelegate(
76 scoped_ptr<LockStateControllerDelegate> delegate) {
77 delegate_ = delegate.Pass();
80 void LockStateController::AddObserver(LockStateObserver* observer) {
81 observers_.AddObserver(observer);
84 void LockStateController::RemoveObserver(LockStateObserver* observer) {
85 observers_.RemoveObserver(observer);
88 bool LockStateController::HasObserver(const LockStateObserver* observer) const {
89 return observers_.HasObserver(observer);
92 void LockStateController::StartLockAnimation(
93 bool shutdown_after_lock) {
94 if (animating_lock_)
95 return;
96 shutdown_after_lock_ = shutdown_after_lock;
97 can_cancel_lock_animation_ = true;
99 StartCancellablePreLockAnimation();
102 void LockStateController::StartShutdownAnimation() {
103 StartCancellableShutdownAnimation();
106 void LockStateController::StartLockAnimationAndLockImmediately(
107 bool shutdown_after_lock) {
108 if (animating_lock_)
109 return;
110 shutdown_after_lock_ = shutdown_after_lock;
111 StartImmediatePreLockAnimation(true /* request_lock_on_completion */);
114 bool LockStateController::LockRequested() {
115 return lock_fail_timer_.IsRunning();
118 bool LockStateController::ShutdownRequested() {
119 return shutting_down_;
122 bool LockStateController::CanCancelLockAnimation() {
123 return can_cancel_lock_animation_;
126 void LockStateController::CancelLockAnimation() {
127 if (!CanCancelLockAnimation())
128 return;
129 shutdown_after_lock_ = false;
130 animating_lock_ = false;
131 CancelPreLockAnimation();
134 bool LockStateController::CanCancelShutdownAnimation() {
135 return pre_shutdown_timer_.IsRunning() ||
136 shutdown_after_lock_ ||
137 lock_to_shutdown_timer_.IsRunning();
140 void LockStateController::CancelShutdownAnimation() {
141 if (!CanCancelShutdownAnimation())
142 return;
143 if (lock_to_shutdown_timer_.IsRunning()) {
144 lock_to_shutdown_timer_.Stop();
145 return;
147 if (shutdown_after_lock_) {
148 shutdown_after_lock_ = false;
149 return;
152 animator_->StartAnimation(
153 SessionStateAnimator::ROOT_CONTAINER,
154 SessionStateAnimator::ANIMATION_UNDO_GRAYSCALE_BRIGHTNESS,
155 SessionStateAnimator::ANIMATION_SPEED_REVERT_SHUTDOWN);
156 pre_shutdown_timer_.Stop();
159 void LockStateController::OnStartingLock() {
160 if (shutting_down_ || system_is_locked_)
161 return;
162 if (animating_lock_)
163 return;
164 StartImmediatePreLockAnimation(false /* request_lock_on_completion */);
167 void LockStateController::RequestShutdown() {
168 if (shutting_down_)
169 return;
171 shutting_down_ = true;
173 Shell* shell = ash::Shell::GetInstance();
174 shell->cursor_manager()->HideCursor();
175 shell->cursor_manager()->LockCursor();
177 animator_->StartAnimation(
178 SessionStateAnimator::ROOT_CONTAINER,
179 SessionStateAnimator::ANIMATION_GRAYSCALE_BRIGHTNESS,
180 SessionStateAnimator::ANIMATION_SPEED_SHUTDOWN);
181 StartRealShutdownTimer(true);
184 void LockStateController::OnLockScreenHide(
185 base::Callback<void(void)>& callback) {
186 StartUnlockAnimationBeforeUIDestroyed(callback);
189 void LockStateController::SetLockScreenDisplayedCallback(
190 const base::Closure& callback) {
191 lock_screen_displayed_callback_ = callback;
194 void LockStateController::OnHostCloseRequested(
195 const aura::WindowTreeHost* host) {
196 Shell::GetInstance()->delegate()->Exit();
199 void LockStateController::OnLoginStateChanged(
200 user::LoginStatus status) {
201 if (status != user::LOGGED_IN_LOCKED)
202 login_status_ = status;
203 system_is_locked_ = (status == user::LOGGED_IN_LOCKED);
206 void LockStateController::OnAppTerminating() {
207 // If we hear that Chrome is exiting but didn't request it ourselves, all we
208 // can really hope for is that we'll have time to clear the screen.
209 // This is also the case when the user signs off.
210 if (!shutting_down_) {
211 shutting_down_ = true;
212 Shell* shell = ash::Shell::GetInstance();
213 shell->cursor_manager()->HideCursor();
214 shell->cursor_manager()->LockCursor();
215 animator_->StartAnimation(SessionStateAnimator::kAllNonRootContainersMask,
216 SessionStateAnimator::ANIMATION_HIDE_IMMEDIATELY,
217 SessionStateAnimator::ANIMATION_SPEED_IMMEDIATE);
221 void LockStateController::OnLockStateChanged(bool locked) {
222 VLOG(1) << "OnLockStateChanged " << locked;
223 if (shutting_down_ || (system_is_locked_ == locked))
224 return;
226 system_is_locked_ = locked;
228 if (locked) {
229 StartPostLockAnimation();
230 lock_fail_timer_.Stop();
231 } else {
232 StartUnlockAnimationAfterUIDestroyed();
236 void LockStateController::OnLockFailTimeout() {
237 DCHECK(!system_is_locked_);
238 CHECK(false) << "We can not be sure about the lock state. Crash and let the "
239 << "SessionManager end the session";
242 void LockStateController::StartLockToShutdownTimer() {
243 shutdown_after_lock_ = false;
244 lock_to_shutdown_timer_.Stop();
245 lock_to_shutdown_timer_.Start(
246 FROM_HERE,
247 base::TimeDelta::FromMilliseconds(kLockToShutdownTimeoutMs),
248 this, &LockStateController::OnLockToShutdownTimeout);
251 void LockStateController::OnLockToShutdownTimeout() {
252 DCHECK(system_is_locked_);
253 StartCancellableShutdownAnimation();
256 void LockStateController::StartPreShutdownAnimationTimer() {
257 pre_shutdown_timer_.Stop();
258 pre_shutdown_timer_.Start(
259 FROM_HERE,
260 animator_->GetDuration(SessionStateAnimator::ANIMATION_SPEED_SHUTDOWN),
261 this,
262 &LockStateController::OnPreShutdownAnimationTimeout);
265 void LockStateController::OnPreShutdownAnimationTimeout() {
266 VLOG(1) << "OnPreShutdownAnimationTimeout";
267 shutting_down_ = true;
269 Shell* shell = ash::Shell::GetInstance();
270 shell->cursor_manager()->HideCursor();
272 StartRealShutdownTimer(false);
275 void LockStateController::StartRealShutdownTimer(bool with_animation_time) {
276 base::TimeDelta duration =
277 base::TimeDelta::FromMilliseconds(kShutdownRequestDelayMs);
278 if (with_animation_time) {
279 duration +=
280 animator_->GetDuration(SessionStateAnimator::ANIMATION_SPEED_SHUTDOWN);
283 #if defined(OS_CHROMEOS)
284 const AccessibilityDelegate* const delegate =
285 Shell::GetInstance()->accessibility_delegate();
286 base::TimeDelta sound_duration = delegate->PlayShutdownSound();
287 sound_duration =
288 std::min(sound_duration,
289 base::TimeDelta::FromMilliseconds(kMaxShutdownSoundDurationMs));
290 duration = std::max(duration, sound_duration);
291 #endif
293 real_shutdown_timer_.Start(
294 FROM_HERE, duration, this, &LockStateController::OnRealShutdownTimeout);
297 void LockStateController::OnRealShutdownTimeout() {
298 VLOG(1) << "OnRealShutdownTimeout";
299 DCHECK(shutting_down_);
300 #if defined(OS_CHROMEOS)
301 if (!base::SysInfo::IsRunningOnChromeOS()) {
302 ShellDelegate* delegate = Shell::GetInstance()->delegate();
303 if (delegate) {
304 delegate->Exit();
305 return;
308 #endif
309 Shell::GetInstance()->metrics()->RecordUserMetricsAction(
310 UMA_ACCEL_SHUT_DOWN_POWER_BUTTON);
311 delegate_->RequestShutdown();
314 void LockStateController::StartCancellableShutdownAnimation() {
315 Shell* shell = ash::Shell::GetInstance();
316 // Hide cursor, but let it reappear if the mouse moves.
317 shell->cursor_manager()->HideCursor();
319 animator_->StartAnimation(
320 SessionStateAnimator::ROOT_CONTAINER,
321 SessionStateAnimator::ANIMATION_GRAYSCALE_BRIGHTNESS,
322 SessionStateAnimator::ANIMATION_SPEED_SHUTDOWN);
323 StartPreShutdownAnimationTimer();
326 void LockStateController::StartImmediatePreLockAnimation(
327 bool request_lock_on_completion) {
328 VLOG(1) << "StartImmediatePreLockAnimation " << request_lock_on_completion;
329 animating_lock_ = true;
330 StoreUnlockedProperties();
332 base::Closure next_animation_starter =
333 base::Bind(&LockStateController::PreLockAnimationFinished,
334 weak_ptr_factory_.GetWeakPtr(),
335 request_lock_on_completion);
336 SessionStateAnimator::AnimationSequence* animation_sequence =
337 animator_->BeginAnimationSequence(next_animation_starter);
339 animation_sequence->StartAnimation(
340 SessionStateAnimator::NON_LOCK_SCREEN_CONTAINERS,
341 SessionStateAnimator::ANIMATION_LIFT,
342 SessionStateAnimator::ANIMATION_SPEED_MOVE_WINDOWS);
343 animation_sequence->StartAnimation(
344 SessionStateAnimator::LAUNCHER,
345 SessionStateAnimator::ANIMATION_FADE_OUT,
346 SessionStateAnimator::ANIMATION_SPEED_MOVE_WINDOWS);
347 // Hide the screen locker containers so we can raise them later.
348 animator_->StartAnimation(SessionStateAnimator::LOCK_SCREEN_CONTAINERS,
349 SessionStateAnimator::ANIMATION_HIDE_IMMEDIATELY,
350 SessionStateAnimator::ANIMATION_SPEED_IMMEDIATE);
351 AnimateBackgroundAppearanceIfNecessary(
352 SessionStateAnimator::ANIMATION_SPEED_MOVE_WINDOWS, animation_sequence);
354 animation_sequence->EndSequence();
356 DispatchCancelMode();
357 FOR_EACH_OBSERVER(LockStateObserver, observers_,
358 OnLockStateEvent(LockStateObserver::EVENT_LOCK_ANIMATION_STARTED));
361 void LockStateController::StartCancellablePreLockAnimation() {
362 animating_lock_ = true;
363 StoreUnlockedProperties();
364 VLOG(1) << "StartCancellablePreLockAnimation";
365 base::Closure next_animation_starter =
366 base::Bind(&LockStateController::PreLockAnimationFinished,
367 weak_ptr_factory_.GetWeakPtr(),
368 true /* request_lock */);
369 SessionStateAnimator::AnimationSequence* animation_sequence =
370 animator_->BeginAnimationSequence(next_animation_starter);
372 animation_sequence->StartAnimation(
373 SessionStateAnimator::NON_LOCK_SCREEN_CONTAINERS,
374 SessionStateAnimator::ANIMATION_LIFT,
375 SessionStateAnimator::ANIMATION_SPEED_UNDOABLE);
376 animation_sequence->StartAnimation(
377 SessionStateAnimator::LAUNCHER,
378 SessionStateAnimator::ANIMATION_FADE_OUT,
379 SessionStateAnimator::ANIMATION_SPEED_UNDOABLE);
380 // Hide the screen locker containers so we can raise them later.
381 animator_->StartAnimation(SessionStateAnimator::LOCK_SCREEN_CONTAINERS,
382 SessionStateAnimator::ANIMATION_HIDE_IMMEDIATELY,
383 SessionStateAnimator::ANIMATION_SPEED_IMMEDIATE);
384 AnimateBackgroundAppearanceIfNecessary(
385 SessionStateAnimator::ANIMATION_SPEED_UNDOABLE, animation_sequence);
387 DispatchCancelMode();
388 FOR_EACH_OBSERVER(LockStateObserver, observers_,
389 OnLockStateEvent(LockStateObserver::EVENT_PRELOCK_ANIMATION_STARTED));
390 animation_sequence->EndSequence();
393 void LockStateController::CancelPreLockAnimation() {
394 VLOG(1) << "CancelPreLockAnimation";
395 base::Closure next_animation_starter =
396 base::Bind(&LockStateController::LockAnimationCancelled,
397 weak_ptr_factory_.GetWeakPtr());
398 SessionStateAnimator::AnimationSequence* animation_sequence =
399 animator_->BeginAnimationSequence(next_animation_starter);
401 animation_sequence->StartAnimation(
402 SessionStateAnimator::NON_LOCK_SCREEN_CONTAINERS,
403 SessionStateAnimator::ANIMATION_UNDO_LIFT,
404 SessionStateAnimator::ANIMATION_SPEED_UNDO_MOVE_WINDOWS);
405 animation_sequence->StartAnimation(
406 SessionStateAnimator::LAUNCHER,
407 SessionStateAnimator::ANIMATION_FADE_IN,
408 SessionStateAnimator::ANIMATION_SPEED_UNDO_MOVE_WINDOWS);
409 AnimateBackgroundHidingIfNecessary(
410 SessionStateAnimator::ANIMATION_SPEED_UNDO_MOVE_WINDOWS,
411 animation_sequence);
413 animation_sequence->EndSequence();
416 void LockStateController::StartPostLockAnimation() {
417 VLOG(1) << "StartPostLockAnimation";
418 base::Closure next_animation_starter =
419 base::Bind(&LockStateController::PostLockAnimationFinished,
420 weak_ptr_factory_.GetWeakPtr());
421 SessionStateAnimator::AnimationSequence* animation_sequence =
422 animator_->BeginAnimationSequence(next_animation_starter);
424 animation_sequence->StartAnimation(
425 SessionStateAnimator::LOCK_SCREEN_CONTAINERS,
426 SessionStateAnimator::ANIMATION_RAISE_TO_SCREEN,
427 SessionStateAnimator::ANIMATION_SPEED_MOVE_WINDOWS);
428 animation_sequence->EndSequence();
431 void LockStateController::StartUnlockAnimationBeforeUIDestroyed(
432 base::Closure& callback) {
433 VLOG(1) << "StartUnlockAnimationBeforeUIDestroyed";
434 animator_->StartAnimationWithCallback(
435 SessionStateAnimator::LOCK_SCREEN_CONTAINERS,
436 SessionStateAnimator::ANIMATION_LIFT,
437 SessionStateAnimator::ANIMATION_SPEED_MOVE_WINDOWS,
438 callback);
441 void LockStateController::StartUnlockAnimationAfterUIDestroyed() {
442 VLOG(1) << "StartUnlockAnimationAfterUIDestroyed";
443 base::Closure next_animation_starter =
444 base::Bind(&LockStateController::UnlockAnimationAfterUIDestroyedFinished,
445 weak_ptr_factory_.GetWeakPtr());
446 SessionStateAnimator::AnimationSequence* animation_sequence =
447 animator_->BeginAnimationSequence(next_animation_starter);
449 animation_sequence->StartAnimation(
450 SessionStateAnimator::NON_LOCK_SCREEN_CONTAINERS,
451 SessionStateAnimator::ANIMATION_DROP,
452 SessionStateAnimator::ANIMATION_SPEED_MOVE_WINDOWS);
453 animation_sequence->StartAnimation(
454 SessionStateAnimator::LAUNCHER,
455 SessionStateAnimator::ANIMATION_FADE_IN,
456 SessionStateAnimator::ANIMATION_SPEED_MOVE_WINDOWS);
457 AnimateBackgroundHidingIfNecessary(
458 SessionStateAnimator::ANIMATION_SPEED_MOVE_WINDOWS, animation_sequence);
459 animation_sequence->EndSequence();
462 void LockStateController::LockAnimationCancelled() {
463 can_cancel_lock_animation_ = false;
464 RestoreUnlockedProperties();
467 void LockStateController::PreLockAnimationFinished(bool request_lock) {
468 VLOG(1) << "PreLockAnimationFinished";
469 can_cancel_lock_animation_ = false;
471 // Don't do anything (including starting the lock-fail timer) if the screen
472 // was already locked while the animation was going.
473 if (system_is_locked_) {
474 DCHECK(!request_lock) << "Got request to lock already-locked system "
475 << "at completion of pre-lock animation";
476 return;
479 if (request_lock) {
480 Shell::GetInstance()->metrics()->RecordUserMetricsAction(
481 shutdown_after_lock_ ?
482 UMA_ACCEL_LOCK_SCREEN_POWER_BUTTON :
483 UMA_ACCEL_LOCK_SCREEN_LOCK_BUTTON);
484 delegate_->RequestLockScreen();
487 base::TimeDelta timeout =
488 base::TimeDelta::FromMilliseconds(kLockFailTimeoutMs);
489 #if defined(OS_CHROMEOS)
490 // Increase lock timeout for slower hardware, see http://crbug.com/350628
491 const std::string board = base::SysInfo::GetLsbReleaseBoard();
492 if (board == "x86-mario" ||
493 StartsWithASCII(board, "x86-alex", true /* case_sensitive */) ||
494 StartsWithASCII(board, "x86-zgb", true /* case_sensitive */)) {
495 timeout *= 2;
497 #endif
498 lock_fail_timer_.Start(
499 FROM_HERE, timeout, this, &LockStateController::OnLockFailTimeout);
502 void LockStateController::PostLockAnimationFinished() {
503 animating_lock_ = false;
504 VLOG(1) << "PostLockAnimationFinished";
505 FOR_EACH_OBSERVER(LockStateObserver, observers_,
506 OnLockStateEvent(LockStateObserver::EVENT_LOCK_ANIMATION_FINISHED));
507 if (!lock_screen_displayed_callback_.is_null()) {
508 lock_screen_displayed_callback_.Run();
509 lock_screen_displayed_callback_.Reset();
511 CHECK(!views::MenuController::GetActiveInstance());
512 if (shutdown_after_lock_) {
513 shutdown_after_lock_ = false;
514 StartLockToShutdownTimer();
518 void LockStateController::UnlockAnimationAfterUIDestroyedFinished() {
519 RestoreUnlockedProperties();
522 void LockStateController::StoreUnlockedProperties() {
523 if (!unlocked_properties_) {
524 unlocked_properties_.reset(new UnlockedStateProperties());
525 unlocked_properties_->background_is_hidden =
526 animator_->IsBackgroundHidden();
528 if (unlocked_properties_->background_is_hidden) {
529 // Hide background so that it can be animated later.
530 animator_->StartAnimation(SessionStateAnimator::DESKTOP_BACKGROUND,
531 SessionStateAnimator::ANIMATION_HIDE_IMMEDIATELY,
532 SessionStateAnimator::ANIMATION_SPEED_IMMEDIATE);
533 animator_->ShowBackground();
537 void LockStateController::RestoreUnlockedProperties() {
538 if (!unlocked_properties_)
539 return;
540 if (unlocked_properties_->background_is_hidden) {
541 animator_->HideBackground();
542 // Restore background visibility.
543 animator_->StartAnimation(SessionStateAnimator::DESKTOP_BACKGROUND,
544 SessionStateAnimator::ANIMATION_FADE_IN,
545 SessionStateAnimator::ANIMATION_SPEED_IMMEDIATE);
547 unlocked_properties_.reset();
550 void LockStateController::AnimateBackgroundAppearanceIfNecessary(
551 SessionStateAnimator::AnimationSpeed speed,
552 SessionStateAnimator::AnimationSequence* animation_sequence) {
553 if (unlocked_properties_.get() &&
554 unlocked_properties_->background_is_hidden) {
555 animation_sequence->StartAnimation(
556 SessionStateAnimator::DESKTOP_BACKGROUND,
557 SessionStateAnimator::ANIMATION_FADE_IN,
558 speed);
562 void LockStateController::AnimateBackgroundHidingIfNecessary(
563 SessionStateAnimator::AnimationSpeed speed,
564 SessionStateAnimator::AnimationSequence* animation_sequence) {
565 if (unlocked_properties_.get() &&
566 unlocked_properties_->background_is_hidden) {
567 animation_sequence->StartAnimation(
568 SessionStateAnimator::DESKTOP_BACKGROUND,
569 SessionStateAnimator::ANIMATION_FADE_OUT,
570 speed);
574 } // namespace ash