1 // Copyright (c) 2012 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
5 #include "chrome/browser/ui/browser_list.h"
6
7 #include <algorithm>
8
9 #include "base/auto_reset.h"
10 #include "base/bind.h"
11 #include "base/check.h"
12 #include "base/metrics/histogram_functions.h"
13 #include "base/metrics/user_metrics.h"
14 #include "chrome/browser/browser_process.h"
15 #include "chrome/browser/chrome_notification_types.h"
16 #include "chrome/browser/lifetime/application_lifetime.h"
17 #include "chrome/browser/lifetime/browser_shutdown.h"
18 #include "chrome/browser/lifetime/termination_notification.h"
19 #include "chrome/browser/profiles/profile.h"
20 #include "chrome/browser/sessions/session_service_factory.h"
21 #include "chrome/browser/ui/browser.h"
22 #include "chrome/browser/ui/browser_finder.h"
23 #include "chrome/browser/ui/browser_list_observer.h"
24 #include "chrome/browser/ui/browser_window.h"
25 #include "content/public/browser/notification_service.h"
26
27 using base::UserMetricsAction;
28 using content::WebContents;
29
30 namespace {
31
GetBrowsersToClose(Profile * profile)32 BrowserList::BrowserVector GetBrowsersToClose(Profile* profile) {
33 BrowserList::BrowserVector browsers_to_close;
34 for (auto* browser : *BrowserList::GetInstance()) {
35 if (browser->profile()->GetOriginalProfile() ==
36 profile->GetOriginalProfile())
37 browsers_to_close.push_back(browser);
38 }
39 return browsers_to_close;
40 }
41
GetIncognitoBrowsersToClose(Profile * profile)42 BrowserList::BrowserVector GetIncognitoBrowsersToClose(Profile* profile) {
43 BrowserList::BrowserVector browsers_to_close;
44 for (auto* browser : *BrowserList::GetInstance()) {
45 if (browser->profile() == profile)
46 browsers_to_close.push_back(browser);
47 }
48 return browsers_to_close;
49 }
50
51 } // namespace
52
53 // static
54 base::LazyInstance<base::ObserverList<BrowserListObserver>::Unchecked>::Leaky
55 BrowserList::observers_ = LAZY_INSTANCE_INITIALIZER;
56
57 // static
58 BrowserList* BrowserList::instance_ = NULL;
59
60 ////////////////////////////////////////////////////////////////////////////////
61 // BrowserList, public:
62
GetLastActive() const63 Browser* BrowserList::GetLastActive() const {
64 if (!last_active_browsers_.empty())
65 return *(last_active_browsers_.rbegin());
66 return NULL;
67 }
68
69 // static
GetInstance()70 BrowserList* BrowserList::GetInstance() {
71 BrowserList** list = &instance_;
72 if (!*list)
73 *list = new BrowserList;
74 return *list;
75 }
76
77 // static
AddBrowser(Browser * browser)78 void BrowserList::AddBrowser(Browser* browser) {
79 DCHECK(browser);
80 DCHECK(browser->window()) << "Browser should not be added to BrowserList "
81 "until it is fully constructed.";
82 GetInstance()->browsers_.push_back(browser);
83
84 browser->RegisterKeepAlive();
85
86 content::NotificationService::current()->Notify(
87 chrome::NOTIFICATION_BROWSER_OPENED, content::Source<Browser>(browser),
88 content::NotificationService::NoDetails());
89
90 for (BrowserListObserver& observer : observers_.Get())
91 observer.OnBrowserAdded(browser);
92
93 if (browser->window()->IsActive())
94 SetLastActive(browser);
95
96 if (browser->profile()->IsGuestSession() ||
97 browser->profile()->IsEphemeralGuestProfile()) {
98 base::UmaHistogramCounts100("Browser.WindowCount.Guest",
99 GetGuestBrowserCount());
100 } else if (browser->profile()->IsIncognitoProfile()) {
101 base::UmaHistogramCounts100(
102 "Browser.WindowCount.Incognito",
103 GetOffTheRecordBrowsersActiveForProfile(browser->profile()));
104 }
105 }
106
107 // static
RemoveBrowser(Browser * browser)108 void BrowserList::RemoveBrowser(Browser* browser) {
109 // Remove |browser| from the appropriate list instance.
110 BrowserList* browser_list = GetInstance();
111 RemoveBrowserFrom(browser, &browser_list->last_active_browsers_);
112 browser_list->currently_closing_browsers_.erase(browser);
113
114 RemoveBrowserFrom(browser, &browser_list->browsers_);
115
116 for (BrowserListObserver& observer : observers_.Get())
117 observer.OnBrowserRemoved(browser);
118
119 browser->UnregisterKeepAlive();
120
121 // If we're exiting, send out the APP_TERMINATING notification to allow other
122 // modules to shut themselves down.
123 if (chrome::GetTotalBrowserCount() == 0 &&
124 (browser_shutdown::IsTryingToQuit() ||
125 g_browser_process->IsShuttingDown())) {
126 // Last browser has just closed, and this is a user-initiated quit or there
127 // is no module keeping the app alive, so send out our notification. No need
128 // to call ProfileManager::ShutdownSessionServices() as part of the
129 // shutdown, because Browser::WindowClosing() already makes sure that the
130 // SessionService is created and notified.
131 browser_shutdown::NotifyAppTerminating();
132 chrome::OnAppExiting();
133 }
134 }
135
136 // static
AddObserver(BrowserListObserver * observer)137 void BrowserList::AddObserver(BrowserListObserver* observer) {
138 observers_.Get().AddObserver(observer);
139 }
140
141 // static
RemoveObserver(BrowserListObserver * observer)142 void BrowserList::RemoveObserver(BrowserListObserver* observer) {
143 observers_.Get().RemoveObserver(observer);
144 }
145
146 // static
CloseAllBrowsersWithProfile(Profile * profile)147 void BrowserList::CloseAllBrowsersWithProfile(Profile* profile) {
148 BrowserVector browsers_to_close;
149 for (auto* browser : *BrowserList::GetInstance()) {
150 if (browser->profile()->GetOriginalProfile() ==
151 profile->GetOriginalProfile())
152 browsers_to_close.push_back(browser);
153 }
154
155 for (BrowserVector::const_iterator it = browsers_to_close.begin();
156 it != browsers_to_close.end(); ++it) {
157 (*it)->window()->Close();
158 }
159 }
160
161 // static
CloseAllBrowsersWithProfile(Profile * profile,const CloseCallback & on_close_success,const CloseCallback & on_close_aborted,bool skip_beforeunload)162 void BrowserList::CloseAllBrowsersWithProfile(
163 Profile* profile,
164 const CloseCallback& on_close_success,
165 const CloseCallback& on_close_aborted,
166 bool skip_beforeunload) {
167 #if BUILDFLAG(ENABLE_SESSION_SERVICE)
168 SessionServiceFactory::ShutdownForProfile(profile);
169 #endif
170
171 TryToCloseBrowserList(GetBrowsersToClose(profile), on_close_success,
172 on_close_aborted, profile->GetPath(),
173 skip_beforeunload);
174 }
175
176 // static
CloseAllBrowsersWithIncognitoProfile(Profile * profile,const CloseCallback & on_close_success,const CloseCallback & on_close_aborted,bool skip_beforeunload)177 void BrowserList::CloseAllBrowsersWithIncognitoProfile(
178 Profile* profile,
179 const CloseCallback& on_close_success,
180 const CloseCallback& on_close_aborted,
181 bool skip_beforeunload) {
182 DCHECK(profile->IsOffTheRecord());
183 BrowserList::BrowserVector browsers_to_close =
184 GetIncognitoBrowsersToClose(profile);
185 auto it =
186 std::find_if(browsers_to_close.begin(), browsers_to_close.end(),
187 [](auto* browser) { return browser->is_type_devtools(); });
188
189 // When closing devtools browser related to incognito browser, do not skip
190 // calling before unload handlers.
191 skip_beforeunload = skip_beforeunload && (it == browsers_to_close.end());
192 TryToCloseBrowserList(browsers_to_close, on_close_success, on_close_aborted,
193 profile->GetPath(), skip_beforeunload);
194 }
195
196 // static
TryToCloseBrowserList(const BrowserVector & browsers_to_close,const CloseCallback & on_close_success,const CloseCallback & on_close_aborted,const base::FilePath & profile_path,const bool skip_beforeunload)197 void BrowserList::TryToCloseBrowserList(const BrowserVector& browsers_to_close,
198 const CloseCallback& on_close_success,
199 const CloseCallback& on_close_aborted,
200 const base::FilePath& profile_path,
201 const bool skip_beforeunload) {
202 for (auto it = browsers_to_close.begin(); it != browsers_to_close.end();
203 ++it) {
204 if ((*it)->TryToCloseWindow(
205 skip_beforeunload,
206 base::Bind(&BrowserList::PostTryToCloseBrowserWindow,
207 browsers_to_close, on_close_success, on_close_aborted,
208 profile_path, skip_beforeunload))) {
209 return;
210 }
211 }
212
213 if (on_close_success)
214 on_close_success.Run(profile_path);
215
216 for (Browser* b : browsers_to_close) {
217 // BeforeUnload handlers may close browser windows, so we need to explicitly
218 // check whether they still exist.
219 if (b->window())
220 b->window()->Close();
221 }
222 }
223
224 // static
PostTryToCloseBrowserWindow(const BrowserVector & browsers_to_close,const CloseCallback & on_close_success,const CloseCallback & on_close_aborted,const base::FilePath & profile_path,const bool skip_beforeunload,bool tab_close_confirmed)225 void BrowserList::PostTryToCloseBrowserWindow(
226 const BrowserVector& browsers_to_close,
227 const CloseCallback& on_close_success,
228 const CloseCallback& on_close_aborted,
229 const base::FilePath& profile_path,
230 const bool skip_beforeunload,
231 bool tab_close_confirmed) {
232 // We need this bool to avoid infinite recursion when resetting the
233 // BeforeUnload handlers, since doing that will trigger calls back to this
234 // method for each affected window.
235 static bool resetting_handlers = false;
236
237 if (tab_close_confirmed) {
238 TryToCloseBrowserList(browsers_to_close, on_close_success, on_close_aborted,
239 profile_path, skip_beforeunload);
240 } else if (!resetting_handlers) {
241 base::AutoReset<bool> resetting_handlers_scoper(&resetting_handlers, true);
242 for (auto it = browsers_to_close.begin(); it != browsers_to_close.end();
243 ++it) {
244 (*it)->ResetTryToCloseWindow();
245 }
246 if (on_close_aborted)
247 on_close_aborted.Run(profile_path);
248 }
249 }
250
251 // static
MoveBrowsersInWorkspaceToFront(const std::string & new_workspace)252 void BrowserList::MoveBrowsersInWorkspaceToFront(
253 const std::string& new_workspace) {
254 DCHECK(!new_workspace.empty());
255
256 BrowserList* instance = GetInstance();
257
258 Browser* old_last_active = instance->GetLastActive();
259 BrowserVector& last_active_browsers = instance->last_active_browsers_;
260
261 // Perform a stable partition on the browsers in the list so that the browsers
262 // in the new workspace appear after the browsers in the other workspaces.
263 //
264 // For example, if we have a list of browser-workspace pairs
265 // [{b1, 0}, {b2, 1}, {b3, 0}, {b4, 1}]
266 // and we switch to workspace 1, we want the resulting browser list to look
267 // like [{b1, 0}, {b3, 0}, {b2, 1}, {b4, 1}].
268 std::stable_partition(
269 last_active_browsers.begin(), last_active_browsers.end(),
270 [&new_workspace](Browser* browser) {
271 return !browser->window()->IsVisibleOnAllWorkspaces() &&
272 browser->window()->GetWorkspace() != new_workspace;
273 });
274
275 Browser* new_last_active = instance->GetLastActive();
276 if (old_last_active != new_last_active) {
277 for (BrowserListObserver& observer : observers_.Get())
278 observer.OnBrowserSetLastActive(new_last_active);
279 }
280 }
281
282 // static
SetLastActive(Browser * browser)283 void BrowserList::SetLastActive(Browser* browser) {
284 BrowserList* instance = GetInstance();
285 DCHECK(std::find(instance->begin(), instance->end(), browser) !=
286 instance->end())
287 << "SetLastActive called for a browser before the browser was added to "
288 "the BrowserList.";
289 DCHECK(browser->window())
290 << "SetLastActive called for a browser with no window set.";
291
292 base::RecordAction(UserMetricsAction("ActiveBrowserChanged"));
293
294 RemoveBrowserFrom(browser, &instance->last_active_browsers_);
295 instance->last_active_browsers_.push_back(browser);
296
297 for (BrowserListObserver& observer : observers_.Get())
298 observer.OnBrowserSetLastActive(browser);
299 }
300
301 // static
NotifyBrowserNoLongerActive(Browser * browser)302 void BrowserList::NotifyBrowserNoLongerActive(Browser* browser) {
303 BrowserList* instance = GetInstance();
304 DCHECK(std::find(instance->begin(), instance->end(), browser) !=
305 instance->end())
306 << "NotifyBrowserNoLongerActive called for a browser before the browser "
307 "was added to the BrowserList.";
308 DCHECK(browser->window())
309 << "NotifyBrowserNoLongerActive called for a browser with no window set.";
310
311 for (BrowserListObserver& observer : observers_.Get())
312 observer.OnBrowserNoLongerActive(browser);
313 }
314
315 // static
NotifyBrowserCloseStarted(Browser * browser)316 void BrowserList::NotifyBrowserCloseStarted(Browser* browser) {
317 GetInstance()->currently_closing_browsers_.insert(browser);
318
319 for (BrowserListObserver& observer : observers_.Get())
320 observer.OnBrowserClosing(browser);
321 }
322
323 // static
IsOffTheRecordBrowserActive()324 bool BrowserList::IsOffTheRecordBrowserActive() {
325 for (auto* browser : *BrowserList::GetInstance()) {
326 if (browser->profile()->IsOffTheRecord())
327 return true;
328 }
329 return false;
330 }
331
332 // static
GetOffTheRecordBrowsersActiveForProfile(Profile * profile)333 int BrowserList::GetOffTheRecordBrowsersActiveForProfile(Profile* profile) {
334 BrowserList* list = BrowserList::GetInstance();
335 return std::count_if(list->begin(), list->end(), [profile](Browser* browser) {
336 return browser->profile()->IsSameOrParent(profile) &&
337 browser->profile()->IsOffTheRecord() && !browser->is_type_devtools();
338 });
339 }
340
341 // static
GetIncognitoBrowserCount()342 size_t BrowserList::GetIncognitoBrowserCount() {
343 BrowserList* list = BrowserList::GetInstance();
344 return std::count_if(list->begin(), list->end(), [](Browser* browser) {
345 return browser->profile()->IsIncognitoProfile() &&
346 !browser->is_type_devtools();
347 });
348 }
349
350 // static
GetGuestBrowserCount()351 size_t BrowserList::GetGuestBrowserCount() {
352 BrowserList* list = BrowserList::GetInstance();
353 return std::count_if(list->begin(), list->end(), [](Browser* browser) {
354 return (browser->profile()->IsGuestSession() ||
355 browser->profile()->IsEphemeralGuestProfile()) &&
356 !browser->is_type_devtools();
357 });
358 }
359
360 // static
IsOffTheRecordBrowserInUse(Profile * profile)361 bool BrowserList::IsOffTheRecordBrowserInUse(Profile* profile) {
362 BrowserList* list = BrowserList::GetInstance();
363 return std::any_of(list->begin(), list->end(), [profile](Browser* browser) {
364 return browser->profile()->IsSameOrParent(profile) &&
365 browser->profile()->IsOffTheRecord();
366 });
367 }
368
369 ////////////////////////////////////////////////////////////////////////////////
370 // BrowserList, private:
371
BrowserList()372 BrowserList::BrowserList() {}
373
~BrowserList()374 BrowserList::~BrowserList() {}
375
376 // static
RemoveBrowserFrom(Browser * browser,BrowserVector * browser_list)377 void BrowserList::RemoveBrowserFrom(Browser* browser,
378 BrowserVector* browser_list) {
379 auto remove_browser =
380 std::find(browser_list->begin(), browser_list->end(), browser);
381 if (remove_browser != browser_list->end())
382 browser_list->erase(remove_browser);
383 }
384