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
|
--- chrome/browser/ui/browser.cc.orig
+++ chrome/browser/ui/browser.cc
@@ -140,6 +140,8 @@
#include "chrome/browser/ui/tabs/tab_strip_model.h"
#include "chrome/browser/ui/tabs/tab_utils.h"
#include "chrome/browser/ui/ui_features.h"
+#include "chrome/browser/ui/views/frame/browser_view.h"
+#include "chrome/browser/ui/views/message_box_dialog.h"
#include "chrome/browser/ui/web_applications/app_browser_controller.h"
#include "chrome/browser/ui/web_applications/web_app_launch_utils.h"
#include "chrome/browser/ui/webui/signin/login_ui_service.h"
@@ -467,6 +469,7 @@ Browser::Browser(const CreateParams& par
omit_from_session_restore_(params.omit_from_session_restore),
should_trigger_session_restore_(params.should_trigger_session_restore),
cancel_download_confirmation_state_(NOT_PROMPTED),
+ close_multitab_confirmation_state_(NOT_PROMPTED),
override_bounds_(params.initial_bounds),
initial_show_state_(params.initial_show_state),
initial_workspace_(params.initial_workspace),
@@ -830,7 +833,7 @@ Browser::WarnBeforeClosingResult Browser
// If the browser can close right away (there are no pending downloads we need
// to prompt about) then there's no need to warn. In the future, we might need
// to check other conditions as well.
- if (CanCloseWithInProgressDownloads())
+ if (CanCloseWithInProgressDownloads() && CanCloseWithMultipleTabs())
return WarnBeforeClosingResult::kOkToClose;
DCHECK(!warn_before_closing_callback_)
@@ -860,6 +863,7 @@ bool Browser::TryToCloseWindow(
void Browser::ResetTryToCloseWindow() {
cancel_download_confirmation_state_ = NOT_PROMPTED;
+ close_multitab_confirmation_state_ = NOT_PROMPTED;
unload_controller_.ResetTryToCloseWindow();
}
@@ -2702,6 +2706,62 @@ bool Browser::CanCloseWithInProgressDown
return false;
}
+bool Browser::CanCloseWithMultipleTabs() {
+ if (!base::CommandLine::ForCurrentProcess()->HasSwitch("close-confirmation"))
+ return true;
+
+ // If we've prompted, we need to hear from the user before we
+ // can close.
+ if (close_multitab_confirmation_state_ != NOT_PROMPTED)
+ return close_multitab_confirmation_state_ != WAITING_FOR_RESPONSE;
+
+ // If we're not running a full browser process with a profile manager
+ // (testing), it's ok to close the browser.
+ if (!g_browser_process->profile_manager())
+ return true;
+
+ // Figure out how many windows are open total
+ int total_window_count = 0;
+ for (auto* browser : *BrowserList::GetInstance()) {
+ // Don't count this browser window or any other in the process of closing.
+ // Window closing may be delayed, and windows that are in the process of
+ // closing don't count against our totals.
+ if (browser == this || browser->IsAttemptingToCloseBrowser())
+ continue;
+ total_window_count++;
+ }
+
+ const auto flag_value = base::CommandLine::ForCurrentProcess()->GetSwitchValueASCII("close-confirmation");
+ bool show_confirmation_last_window = flag_value == "last";
+
+ if (show_confirmation_last_window) {
+ if (total_window_count >= 1 || this->tab_strip_model()->count() <= 1)
+ return true;
+ } else {
+ if (total_window_count == 0)
+ return true;
+ if (this->tab_strip_model()->count() == 0)
+ tab_strip_model_delegate_->AddTabAt(GURL(), -1, true);
+ }
+
+ close_multitab_confirmation_state_ = WAITING_FOR_RESPONSE;
+
+ // The dialog eats mouse events which results in the close button
+ // getting stuck in the hover state. Reset the window controls to
+ // prevent this.
+ ((BrowserView*)window_)->frame()->non_client_view()->ResetWindowControls();
+ auto callback = base::BindOnce(&Browser::MultitabResponse,
+ weak_factory_.GetWeakPtr());
+ MessageBoxDialog::Show(window_->GetNativeWindow(),
+ u"Do you want to close this window?", std::u16string(),
+ chrome::MESSAGE_BOX_TYPE_QUESTION, u"Close", u"Cancel",
+ std::u16string(), std::move(callback));
+
+ // Return false so the browser does not close. We'll close if the user
+ // confirms in the dialog.
+ return false;
+}
+
void Browser::InProgressDownloadResponse(bool cancel_downloads) {
if (cancel_downloads) {
cancel_download_confirmation_state_ = RESPONSE_RECEIVED;
@@ -2720,6 +2780,22 @@ void Browser::InProgressDownloadResponse
std::move(warn_before_closing_callback_)
.Run(WarnBeforeClosingResult::kDoNotClose);
+}
+
+void Browser::MultitabResponse(chrome::MessageBoxResult result) {
+ if (result == chrome::MESSAGE_BOX_RESULT_YES) {
+ close_multitab_confirmation_state_ = RESPONSE_RECEIVED;
+ std::move(warn_before_closing_callback_)
+ .Run(WarnBeforeClosingResult::kOkToClose);
+ return;
+ }
+
+ // Sets the confirmation state to NOT_PROMPTED so that if the user tries to
+ // close again we'll show the warning again.
+ close_multitab_confirmation_state_ = NOT_PROMPTED;
+
+ std::move(warn_before_closing_callback_)
+ .Run(WarnBeforeClosingResult::kDoNotClose);
}
void Browser::FinishWarnBeforeClosing(WarnBeforeClosingResult result) {
--- chrome/browser/ui/browser.h.orig
+++ chrome/browser/ui/browser.h
@@ -26,6 +26,7 @@
#include "chrome/browser/ui/bookmarks/bookmark_tab_helper_observer.h"
#include "chrome/browser/ui/browser_navigator_params.h"
#include "chrome/browser/ui/chrome_web_modal_dialog_manager_delegate.h"
+#include "chrome/browser/ui/simple_message_box.h"
#include "chrome/browser/ui/signin_view_controller.h"
#include "chrome/browser/ui/tabs/tab_strip_model_observer.h"
#include "chrome/browser/ui/unload_controller.h"
@@ -1008,12 +1009,17 @@ class Browser : public TabStripModelObse
// Returns true if the window can close, false otherwise.
bool CanCloseWithInProgressDownloads();
+ // Called when the window is closing to check if more than one tabs are open
+ bool CanCloseWithMultipleTabs();
+
// Called when the user has decided whether to proceed or not with the browser
// closure. |cancel_downloads| is true if the downloads should be canceled
// and the browser closed, false if the browser should stay open and the
// downloads running.
void InProgressDownloadResponse(bool cancel_downloads);
+ void MultitabResponse(chrome::MessageBoxResult result);
+
// Called when all warnings have completed when attempting to close the
// browser directly (e.g. via hotkey, close button, terminate signal, etc.)
// Used as a WarnBeforeClosingCallback by ShouldCloseWindow().
@@ -1176,6 +1182,8 @@ class Browser : public TabStripModelObse
// when the browser is closed with in-progress downloads.
CancelDownloadConfirmationState cancel_download_confirmation_state_;
+ CancelDownloadConfirmationState close_multitab_confirmation_state_;
+
/////////////////////////////////////////////////////////////////////////////
// Override values for the bounds of the window and its maximized or minimized
--- chrome/browser/ungoogled_flag_choices.h.orig
+++ chrome/browser/ungoogled_flag_choices.h
@@ -66,4 +66,13 @@ const FeatureEntry::Choice kCloseWindowW
"close-window-with-last-tab",
"never"},
};
+const FeatureEntry::Choice kCloseConfirmation[] = {
+ {flags_ui::kGenericExperimentChoiceDefault, "", ""},
+ {"Show confirmation with last window",
+ "close-confirmation",
+ "last"},
+ {"Show confirmation with multiple windows",
+ "close-confirmation",
+ "multiple"},
+};
#endif // CHROME_BROWSER_UNGOOGLED_FLAG_CHOICES_H_
--- chrome/browser/ungoogled_flag_entries.h.orig
+++ chrome/browser/ungoogled_flag_entries.h
@@ -80,4 +80,8 @@
"Remove Grab Handle",
"Removes the reserved empty space in the tabstrip for moving the window. ungoogled-chromium flag",
kOsDesktop, SINGLE_VALUE_TYPE("remove-grab-handle")},
+ {"close-confirmation",
+ "Close Confirmation",
+ "Show a warning prompt when closing the browser window. ungoogled-chromium flag",
+ kOsDesktop, MULTI_VALUE_TYPE(kCloseConfirmation)},
#endif // CHROME_BROWSER_UNGOOGLED_FLAG_ENTRIES_H_
|