1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210
|
// Copyright 2013 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "chrome/browser/lifetime/browser_close_manager.h"
#include <algorithm>
#include <iterator>
#include <ranges>
#include <vector>
#include "base/containers/contains.h"
#include "base/functional/bind.h"
#include "build/build_config.h"
#include "chrome/browser/background/extensions/background_mode_manager.h"
#include "chrome/browser/browser_process.h"
#include "chrome/browser/download/download_core_service.h"
#include "chrome/browser/download/download_core_service_factory.h"
#include "chrome/browser/lifetime/application_lifetime.h"
#include "chrome/browser/lifetime/browser_shutdown.h"
#include "chrome/browser/profiles/profile_manager.h"
#include "chrome/browser/ui/browser.h"
#include "chrome/browser/ui/browser_list.h"
#include "chrome/browser/ui/browser_window.h"
#include "chrome/browser/ui/chrome_pages.h"
#include "chrome/browser/ui/scoped_tabbed_browser_displayer.h"
#include "chrome/browser/ui/tabs/tab_strip_model.h"
#include "chrome/common/buildflags.h"
#include "content/public/browser/web_contents.h"
#if BUILDFLAG(ENABLE_CHROME_NOTIFICATIONS)
#include "chrome/browser/notifications/notification_ui_manager.h"
#endif
#if BUILDFLAG(ENABLE_GLIC)
#include "chrome/browser/background/glic/glic_background_mode_manager.h"
#endif
namespace {
// Navigates a browser window for |profile|, creating one if necessary, to the
// downloads page if there are downloads in progress for |profile|.
void ShowInProgressDownloads(Profile* profile) {
DownloadCoreService* download_core_service =
DownloadCoreServiceFactory::GetForBrowserContext(profile);
if (download_core_service &&
download_core_service->BlockingShutdownCount() > 0) {
chrome::ScopedTabbedBrowserDisplayer displayer(profile);
chrome::ShowDownloads(displayer.browser());
}
}
} // namespace
BrowserCloseManager::BrowserCloseManager() : current_browser_(nullptr) {}
BrowserCloseManager::~BrowserCloseManager() = default;
void BrowserCloseManager::StartClosingBrowsers() {
// If the session is ending or a silent exit was requested, skip straight to
// closing the browsers without waiting for beforeunload dialogs.
if (browser_shutdown::ShouldIgnoreUnloadHandlers()) {
// Tell everyone that we are shutting down.
browser_shutdown::SetTryingToQuit(true);
CloseBrowsers();
return;
}
TryToCloseBrowsers();
}
void BrowserCloseManager::CancelBrowserClose() {
browser_shutdown::SetTryingToQuit(false);
for (Browser* browser : *BrowserList::GetInstance()) {
browser->ResetTryToCloseWindow();
}
}
void BrowserCloseManager::TryToCloseBrowsers() {
// If all browser windows can immediately be closed, fall out of this loop and
// close the browsers. If any browser window cannot be closed, temporarily
// stop closing. CallBeforeUnloadHandlers prompts the user and calls
// OnBrowserReportCloseable with the result. If the user confirms the close,
// this will trigger TryToCloseBrowsers to try again.
for (Browser* browser : *BrowserList::GetInstance()) {
if (browser->TryToCloseWindow(
false, base::BindRepeating(
&BrowserCloseManager::OnBrowserReportCloseable, this))) {
current_browser_ = browser;
return;
}
}
CheckForDownloadsInProgress();
}
void BrowserCloseManager::OnBrowserReportCloseable(bool proceed) {
if (!current_browser_) {
return;
}
current_browser_ = nullptr;
if (proceed) {
TryToCloseBrowsers();
} else {
CancelBrowserClose();
}
}
void BrowserCloseManager::CheckForDownloadsInProgress() {
#if BUILDFLAG(IS_MAC)
// Mac has its own in-progress downloads prompt in app_controller_mac.mm.
CloseBrowsers();
#else
int download_count = DownloadCoreService::BlockingShutdownCountAllProfiles();
if (download_count == 0) {
CloseBrowsers();
return;
}
ConfirmCloseWithPendingDownloads(
download_count,
base::BindOnce(&BrowserCloseManager::OnReportDownloadsCancellable, this));
#endif
}
void BrowserCloseManager::ConfirmCloseWithPendingDownloads(
int download_count,
base::OnceCallback<void(bool)> callback) {
Browser* browser = BrowserList::GetInstance()->GetLastActive();
if (browser == nullptr) {
// Background may call CloseAllBrowsers() with no Browsers. In this
// case immediately continue with shutting down.
std::move(callback).Run(/* proceed= */ true);
return;
}
browser->window()->ConfirmBrowserCloseWithPendingDownloads(
download_count, Browser::DownloadCloseType::kBrowserShutdown,
std::move(callback));
}
void BrowserCloseManager::OnReportDownloadsCancellable(bool proceed) {
if (proceed) {
CloseBrowsers();
return;
}
CancelBrowserClose();
// Open the downloads page for each profile with downloads in progress.
std::vector<Profile*> profiles(
g_browser_process->profile_manager()->GetLoadedProfiles());
for (Profile* profile : profiles) {
ShowInProgressDownloads(profile);
std::vector<Profile*> otr_profiles = profile->GetAllOffTheRecordProfiles();
for (Profile* otr : otr_profiles) {
ShowInProgressDownloads(otr);
}
}
}
void BrowserCloseManager::CloseBrowsers() {
#if BUILDFLAG(ENABLE_SESSION_SERVICE)
// Before we close the browsers shutdown all session services. That way an
// exit can restore all browsers open before exiting.
ProfileManager::ShutdownSessionServices();
#endif
#if BUILDFLAG(ENABLE_BACKGROUND_MODE)
if (!browser_shutdown::IsTryingToQuit()) {
BackgroundModeManager* background_mode_manager =
g_browser_process->background_mode_manager();
if (background_mode_manager) {
background_mode_manager->SuspendBackgroundMode();
}
}
#endif
#if BUILDFLAG(ENABLE_GLIC)
auto* glic_background_mode_manager =
glic::GlicBackgroundModeManager::GetInstance();
if (glic_background_mode_manager) {
glic_background_mode_manager->ExitBackgroundMode();
}
#endif
BrowserList::GetInstance()->ForEachCurrentAndNewBrowser([](Browser* browser) {
bool ignore_unload_handlers =
browser_shutdown::ShouldIgnoreUnloadHandlers();
browser->set_force_skip_warning_user_on_close(ignore_unload_handlers);
browser->window()->Close();
if (ignore_unload_handlers) {
// This path is hit during logoff/power-down. It could be the case that
// there are some tabs which would have prevented the browser from closing
// (Ex: A form with an open dialog asking for permission to leave the
// current site). Since we are attempting to end the session, we will
// force skip these warnings and manually close all the tabs to make sure
// the browser is destroyed and cleanup can happen.
browser->tab_strip_model()->CloseAllTabs();
browser->window()->DestroyBrowser();
// Destroying the browser should have removed it from the browser list.
DCHECK(!base::Contains(*BrowserList::GetInstance(), browser));
}
});
#if BUILDFLAG(ENABLE_CHROME_NOTIFICATIONS)
NotificationUIManager* notification_manager =
g_browser_process->notification_ui_manager();
if (notification_manager) {
notification_manager->CancelAll();
}
#endif
}
|