logout_confirmation_controller.cc 8.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234
  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. #include "ash/system/session/logout_confirmation_controller.h"
  5. #include <memory>
  6. #include <utility>
  7. #include <vector>
  8. #include "ash/constants/ash_pref_names.h"
  9. #include "ash/login_status.h"
  10. #include "ash/public/cpp/shell_window_ids.h"
  11. #include "ash/session/session_controller_impl.h"
  12. #include "ash/shell.h"
  13. #include "ash/shell_observer.h"
  14. #include "ash/system/session/logout_confirmation_dialog.h"
  15. #include "ash/wm/desks/desks_util.h"
  16. #include "base/bind.h"
  17. #include "base/callback.h"
  18. #include "base/location.h"
  19. #include "base/metrics/user_metrics.h"
  20. #include "base/time/default_tick_clock.h"
  21. #include "base/time/tick_clock.h"
  22. #include "components/prefs/pref_registry_simple.h"
  23. #include "components/prefs/pref_service.h"
  24. #include "ui/aura/window.h"
  25. #include "ui/aura/window_observer.h"
  26. #include "ui/views/widget/widget.h"
  27. namespace ash {
  28. namespace {
  29. const int kLogoutConfirmationDelayInSeconds = 20;
  30. std::vector<int> GetLastWindowClosedContainerIds() {
  31. const auto& desks_ids = desks_util::GetDesksContainersIds();
  32. std::vector<int> ids{desks_ids.begin(), desks_ids.end()};
  33. ids.emplace_back(kShellWindowId_AlwaysOnTopContainer);
  34. ids.emplace_back(kShellWindowId_PipContainer);
  35. return ids;
  36. }
  37. void SignOut(LogoutConfirmationController::Source source) {
  38. if (Shell::Get()->session_controller()->IsDemoSession() &&
  39. source == LogoutConfirmationController::Source::kShelfExitButton) {
  40. base::RecordAction(base::UserMetricsAction("DemoMode.ExitFromShelf"));
  41. }
  42. Shell::Get()->session_controller()->RequestSignOut();
  43. }
  44. } // namespace
  45. // Monitors window containers to detect when the last browser or app window is
  46. // closing and shows a logout confirmation dialog.
  47. class LogoutConfirmationController::LastWindowClosedObserver
  48. : public ShellObserver,
  49. public aura::WindowObserver {
  50. public:
  51. LastWindowClosedObserver() {
  52. DCHECK_EQ(Shell::Get()->session_controller()->login_status(),
  53. LoginStatus::PUBLIC);
  54. DCHECK(!Shell::Get()->session_controller()->IsDemoSession());
  55. Shell::Get()->AddShellObserver(this);
  56. // Observe all displays.
  57. for (aura::Window* root : Shell::GetAllRootWindows())
  58. ObserveForLastWindowClosed(root);
  59. }
  60. LastWindowClosedObserver(const LastWindowClosedObserver&) = delete;
  61. LastWindowClosedObserver& operator=(const LastWindowClosedObserver&) = delete;
  62. ~LastWindowClosedObserver() override {
  63. // Stop observing all displays.
  64. for (aura::Window* root : Shell::GetAllRootWindows()) {
  65. for (int id : GetLastWindowClosedContainerIds())
  66. root->GetChildById(id)->RemoveObserver(this);
  67. }
  68. Shell::Get()->RemoveShellObserver(this);
  69. }
  70. private:
  71. // Observes containers in the |root| window for the last browser and/or app
  72. // window being closed. The observers are removed automatically.
  73. void ObserveForLastWindowClosed(aura::Window* root) {
  74. for (int id : GetLastWindowClosedContainerIds())
  75. root->GetChildById(id)->AddObserver(this);
  76. }
  77. bool ShouldShowDialogIfLastWindowClosing() const {
  78. PrefService* prefs =
  79. Shell::Get()->session_controller()->GetLastActiveUserPrefService();
  80. return prefs->GetBoolean(prefs::kSuggestLogoutAfterClosingLastWindow);
  81. }
  82. // Shows the logout confirmation dialog if the last window is closing in the
  83. // containers we are tracking. Called before closing instead of after closed
  84. // because aura::WindowObserver only provides notifications to parent windows
  85. // before a child is removed, not after. Note that removing window deep inside
  86. // tracked container also causes OnWindowHierarchyChanging calls so we check
  87. // here that removing window is the last window with parent of a tracked
  88. // container.
  89. void ShowDialogIfLastWindowClosing(const aura::Window* closing_window) {
  90. // Enumerate all root windows.
  91. for (aura::Window* root : Shell::GetAllRootWindows()) {
  92. // For each root window enumerate tracked containers.
  93. for (int id : GetLastWindowClosedContainerIds()) {
  94. // In each container try to find child window that is not equal to
  95. // |closing_window| which would indicate that we have other top-level
  96. // window and logout time does not apply.
  97. for (const aura::Window* window : root->GetChildById(id)->children()) {
  98. if (window != closing_window)
  99. return;
  100. }
  101. }
  102. }
  103. // No more windows except currently removing. Show logout time.
  104. Shell::Get()->logout_confirmation_controller()->ConfirmLogout(
  105. base::TimeTicks::Now() +
  106. base::Seconds(kLogoutConfirmationDelayInSeconds),
  107. Source::kCloseAllWindows);
  108. }
  109. // ShellObserver:
  110. void OnRootWindowAdded(aura::Window* root) override {
  111. ObserveForLastWindowClosed(root);
  112. }
  113. // aura::WindowObserver:
  114. void OnWindowHierarchyChanging(const HierarchyChangeParams& params) override {
  115. if (!params.new_parent && params.old_parent) {
  116. // A window is being removed (and not moved to another container).
  117. if (ShouldShowDialogIfLastWindowClosing())
  118. ShowDialogIfLastWindowClosing(params.target);
  119. }
  120. }
  121. void OnWindowDestroying(aura::Window* window) override {
  122. // Stop observing the container window when it closes.
  123. window->RemoveObserver(this);
  124. }
  125. };
  126. LogoutConfirmationController::LogoutConfirmationController()
  127. : clock_(base::DefaultTickClock::GetInstance()),
  128. logout_callback_(base::BindRepeating(&SignOut)) {
  129. if (Shell::HasInstance()) // Null in testing::Test.
  130. Shell::Get()->session_controller()->AddObserver(this);
  131. }
  132. LogoutConfirmationController::~LogoutConfirmationController() {
  133. if (dialog_)
  134. dialog_->ControllerGone();
  135. if (Shell::HasInstance()) // Null in testing::Test.
  136. Shell::Get()->session_controller()->RemoveObserver(this);
  137. }
  138. // static
  139. void LogoutConfirmationController::RegisterProfilePrefs(
  140. PrefRegistrySimple* registry) {
  141. registry->RegisterBooleanPref(prefs::kSuggestLogoutAfterClosingLastWindow,
  142. true);
  143. }
  144. void LogoutConfirmationController::ConfirmLogout(base::TimeTicks logout_time,
  145. Source source) {
  146. if (!logout_time_.is_null() && logout_time >= logout_time_) {
  147. // If a confirmation dialog is already being shown and its countdown expires
  148. // no later than the |logout_time| requested now, keep the current dialog
  149. // open.
  150. return;
  151. }
  152. logout_time_ = logout_time;
  153. if (!dialog_) {
  154. // Show confirmation dialog unless this is a unit test without a Shell.
  155. if (Shell::HasInstance())
  156. dialog_ = new LogoutConfirmationDialog(this, logout_time_);
  157. } else {
  158. dialog_->Update(logout_time_);
  159. }
  160. source_ = source;
  161. logout_timer_.Start(FROM_HERE, logout_time_ - clock_->NowTicks(),
  162. base::BindOnce(logout_callback_, source));
  163. ++confirm_logout_count_for_test_;
  164. }
  165. void LogoutConfirmationController::OnLoginStatusChanged(
  166. LoginStatus login_status) {
  167. if (login_status == LoginStatus::PUBLIC &&
  168. !Shell::Get()->session_controller()->IsDemoSession()) {
  169. last_window_closed_observer_ = std::make_unique<LastWindowClosedObserver>();
  170. } else {
  171. last_window_closed_observer_.reset();
  172. }
  173. }
  174. void LogoutConfirmationController::OnLockStateChanged(bool locked) {
  175. if (!locked || logout_time_.is_null())
  176. return;
  177. // If the screen is locked while a confirmation dialog is being shown, close
  178. // the dialog.
  179. logout_time_ = base::TimeTicks();
  180. if (dialog_)
  181. dialog_->GetWidget()->Close();
  182. logout_timer_.Stop();
  183. }
  184. void LogoutConfirmationController::OnLogoutConfirmed() {
  185. logout_timer_.Stop();
  186. logout_callback_.Run(source_);
  187. }
  188. void LogoutConfirmationController::OnDialogClosed() {
  189. logout_time_ = base::TimeTicks();
  190. dialog_ = NULL;
  191. logout_timer_.Stop();
  192. }
  193. void LogoutConfirmationController::SetClockForTesting(
  194. const base::TickClock* clock) {
  195. clock_ = clock;
  196. }
  197. void LogoutConfirmationController::SetLogoutCallbackForTesting(
  198. const base::RepeatingCallback<void(Source)>& logout_callback) {
  199. logout_callback_ = logout_callback;
  200. }
  201. } // namespace ash