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