123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234 |
- // Copyright 2014 The Chromium Authors. All rights reserved.
- // Use of this source code is governed by a BSD-style license that can be
- // found in the LICENSE file.
- #include "ash/system/session/logout_confirmation_controller.h"
- #include <memory>
- #include <utility>
- #include <vector>
- #include "ash/constants/ash_pref_names.h"
- #include "ash/login_status.h"
- #include "ash/public/cpp/shell_window_ids.h"
- #include "ash/session/session_controller_impl.h"
- #include "ash/shell.h"
- #include "ash/shell_observer.h"
- #include "ash/system/session/logout_confirmation_dialog.h"
- #include "ash/wm/desks/desks_util.h"
- #include "base/bind.h"
- #include "base/callback.h"
- #include "base/location.h"
- #include "base/metrics/user_metrics.h"
- #include "base/time/default_tick_clock.h"
- #include "base/time/tick_clock.h"
- #include "components/prefs/pref_registry_simple.h"
- #include "components/prefs/pref_service.h"
- #include "ui/aura/window.h"
- #include "ui/aura/window_observer.h"
- #include "ui/views/widget/widget.h"
- namespace ash {
- namespace {
- const int kLogoutConfirmationDelayInSeconds = 20;
- std::vector<int> GetLastWindowClosedContainerIds() {
- const auto& desks_ids = desks_util::GetDesksContainersIds();
- std::vector<int> ids{desks_ids.begin(), desks_ids.end()};
- ids.emplace_back(kShellWindowId_AlwaysOnTopContainer);
- ids.emplace_back(kShellWindowId_PipContainer);
- return ids;
- }
- void SignOut(LogoutConfirmationController::Source source) {
- if (Shell::Get()->session_controller()->IsDemoSession() &&
- source == LogoutConfirmationController::Source::kShelfExitButton) {
- base::RecordAction(base::UserMetricsAction("DemoMode.ExitFromShelf"));
- }
- Shell::Get()->session_controller()->RequestSignOut();
- }
- } // namespace
- // Monitors window containers to detect when the last browser or app window is
- // closing and shows a logout confirmation dialog.
- class LogoutConfirmationController::LastWindowClosedObserver
- : public ShellObserver,
- public aura::WindowObserver {
- public:
- LastWindowClosedObserver() {
- DCHECK_EQ(Shell::Get()->session_controller()->login_status(),
- LoginStatus::PUBLIC);
- DCHECK(!Shell::Get()->session_controller()->IsDemoSession());
- Shell::Get()->AddShellObserver(this);
- // Observe all displays.
- for (aura::Window* root : Shell::GetAllRootWindows())
- ObserveForLastWindowClosed(root);
- }
- LastWindowClosedObserver(const LastWindowClosedObserver&) = delete;
- LastWindowClosedObserver& operator=(const LastWindowClosedObserver&) = delete;
- ~LastWindowClosedObserver() override {
- // Stop observing all displays.
- for (aura::Window* root : Shell::GetAllRootWindows()) {
- for (int id : GetLastWindowClosedContainerIds())
- root->GetChildById(id)->RemoveObserver(this);
- }
- Shell::Get()->RemoveShellObserver(this);
- }
- private:
- // Observes containers in the |root| window for the last browser and/or app
- // window being closed. The observers are removed automatically.
- void ObserveForLastWindowClosed(aura::Window* root) {
- for (int id : GetLastWindowClosedContainerIds())
- root->GetChildById(id)->AddObserver(this);
- }
- bool ShouldShowDialogIfLastWindowClosing() const {
- PrefService* prefs =
- Shell::Get()->session_controller()->GetLastActiveUserPrefService();
- return prefs->GetBoolean(prefs::kSuggestLogoutAfterClosingLastWindow);
- }
- // Shows the logout confirmation dialog if the last window is closing in the
- // containers we are tracking. Called before closing instead of after closed
- // because aura::WindowObserver only provides notifications to parent windows
- // before a child is removed, not after. Note that removing window deep inside
- // tracked container also causes OnWindowHierarchyChanging calls so we check
- // here that removing window is the last window with parent of a tracked
- // container.
- void ShowDialogIfLastWindowClosing(const aura::Window* closing_window) {
- // Enumerate all root windows.
- for (aura::Window* root : Shell::GetAllRootWindows()) {
- // For each root window enumerate tracked containers.
- for (int id : GetLastWindowClosedContainerIds()) {
- // In each container try to find child window that is not equal to
- // |closing_window| which would indicate that we have other top-level
- // window and logout time does not apply.
- for (const aura::Window* window : root->GetChildById(id)->children()) {
- if (window != closing_window)
- return;
- }
- }
- }
- // No more windows except currently removing. Show logout time.
- Shell::Get()->logout_confirmation_controller()->ConfirmLogout(
- base::TimeTicks::Now() +
- base::Seconds(kLogoutConfirmationDelayInSeconds),
- Source::kCloseAllWindows);
- }
- // ShellObserver:
- void OnRootWindowAdded(aura::Window* root) override {
- ObserveForLastWindowClosed(root);
- }
- // aura::WindowObserver:
- void OnWindowHierarchyChanging(const HierarchyChangeParams& params) override {
- if (!params.new_parent && params.old_parent) {
- // A window is being removed (and not moved to another container).
- if (ShouldShowDialogIfLastWindowClosing())
- ShowDialogIfLastWindowClosing(params.target);
- }
- }
- void OnWindowDestroying(aura::Window* window) override {
- // Stop observing the container window when it closes.
- window->RemoveObserver(this);
- }
- };
- LogoutConfirmationController::LogoutConfirmationController()
- : clock_(base::DefaultTickClock::GetInstance()),
- logout_callback_(base::BindRepeating(&SignOut)) {
- if (Shell::HasInstance()) // Null in testing::Test.
- Shell::Get()->session_controller()->AddObserver(this);
- }
- LogoutConfirmationController::~LogoutConfirmationController() {
- if (dialog_)
- dialog_->ControllerGone();
- if (Shell::HasInstance()) // Null in testing::Test.
- Shell::Get()->session_controller()->RemoveObserver(this);
- }
- // static
- void LogoutConfirmationController::RegisterProfilePrefs(
- PrefRegistrySimple* registry) {
- registry->RegisterBooleanPref(prefs::kSuggestLogoutAfterClosingLastWindow,
- true);
- }
- void LogoutConfirmationController::ConfirmLogout(base::TimeTicks logout_time,
- Source source) {
- if (!logout_time_.is_null() && logout_time >= logout_time_) {
- // If a confirmation dialog is already being shown and its countdown expires
- // no later than the |logout_time| requested now, keep the current dialog
- // open.
- return;
- }
- logout_time_ = logout_time;
- if (!dialog_) {
- // Show confirmation dialog unless this is a unit test without a Shell.
- if (Shell::HasInstance())
- dialog_ = new LogoutConfirmationDialog(this, logout_time_);
- } else {
- dialog_->Update(logout_time_);
- }
- source_ = source;
- logout_timer_.Start(FROM_HERE, logout_time_ - clock_->NowTicks(),
- base::BindOnce(logout_callback_, source));
- ++confirm_logout_count_for_test_;
- }
- void LogoutConfirmationController::OnLoginStatusChanged(
- LoginStatus login_status) {
- if (login_status == LoginStatus::PUBLIC &&
- !Shell::Get()->session_controller()->IsDemoSession()) {
- last_window_closed_observer_ = std::make_unique<LastWindowClosedObserver>();
- } else {
- last_window_closed_observer_.reset();
- }
- }
- void LogoutConfirmationController::OnLockStateChanged(bool locked) {
- if (!locked || logout_time_.is_null())
- return;
- // If the screen is locked while a confirmation dialog is being shown, close
- // the dialog.
- logout_time_ = base::TimeTicks();
- if (dialog_)
- dialog_->GetWidget()->Close();
- logout_timer_.Stop();
- }
- void LogoutConfirmationController::OnLogoutConfirmed() {
- logout_timer_.Stop();
- logout_callback_.Run(source_);
- }
- void LogoutConfirmationController::OnDialogClosed() {
- logout_time_ = base::TimeTicks();
- dialog_ = NULL;
- logout_timer_.Stop();
- }
- void LogoutConfirmationController::SetClockForTesting(
- const base::TickClock* clock) {
- clock_ = clock;
- }
- void LogoutConfirmationController::SetLogoutCallbackForTesting(
- const base::RepeatingCallback<void(Source)>& logout_callback) {
- logout_callback_ = logout_callback;
- }
- } // namespace ash
|