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/logging.h"
11 #include "chrome/browser/browser_process.h"
12 #include "chrome/browser/browser_shutdown.h"
13 #include "chrome/browser/chrome_notification_types.h"
14 #include "chrome/browser/lifetime/application_lifetime.h"
15 #include "chrome/browser/profiles/profile.h"
16 #include "chrome/browser/ui/browser.h"
17 #include "chrome/browser/ui/browser_finder.h"
18 #include "chrome/browser/ui/browser_iterator.h"
19 #include "chrome/browser/ui/browser_list_observer.h"
20 #include "chrome/browser/ui/browser_window.h"
21 #include "chrome/browser/ui/host_desktop.h"
22 #include "content/public/browser/notification_service.h"
23 #include "content/public/browser/user_metrics.h"
24
25 using base::UserMetricsAction;
26 using content::WebContents;
27
28 // static
29 base::LazyInstance<ObserverList<chrome::BrowserListObserver> >::Leaky
30 BrowserList::observers_ = LAZY_INSTANCE_INITIALIZER;
31
32 // static
33 BrowserList* BrowserList::native_instance_ = NULL;
34 BrowserList* BrowserList::ash_instance_ = NULL;
35
36 ////////////////////////////////////////////////////////////////////////////////
37 // BrowserList, public:
38
GetLastActive() const39 Browser* BrowserList::GetLastActive() const {
40 if (!last_active_browsers_.empty())
41 return *(last_active_browsers_.rbegin());
42 return NULL;
43 }
44
45 // static
GetInstance(chrome::HostDesktopType type)46 BrowserList* BrowserList::GetInstance(chrome::HostDesktopType type) {
47 BrowserList** list = NULL;
48 if (type == chrome::HOST_DESKTOP_TYPE_NATIVE)
49 list = &native_instance_;
50 else if (type == chrome::HOST_DESKTOP_TYPE_ASH)
51 list = &ash_instance_;
52 else
53 NOTREACHED();
54 if (!*list)
55 *list = new BrowserList;
56 return *list;
57 }
58
59 // static
AddBrowser(Browser * browser)60 void BrowserList::AddBrowser(Browser* browser) {
61 DCHECK(browser);
62 // Push |browser| on the appropriate list instance.
63 BrowserList* browser_list = GetInstance(browser->host_desktop_type());
64 browser_list->browsers_.push_back(browser);
65
66 g_browser_process->AddRefModule();
67
68 content::NotificationService::current()->Notify(
69 chrome::NOTIFICATION_BROWSER_OPENED,
70 content::Source<Browser>(browser),
71 content::NotificationService::NoDetails());
72
73 FOR_EACH_OBSERVER(chrome::BrowserListObserver, observers_.Get(),
74 OnBrowserAdded(browser));
75 }
76
77 // static
RemoveBrowser(Browser * browser)78 void BrowserList::RemoveBrowser(Browser* browser) {
79 // Remove |browser| from the appropriate list instance.
80 BrowserList* browser_list = GetInstance(browser->host_desktop_type());
81 RemoveBrowserFrom(browser, &browser_list->last_active_browsers_);
82
83 content::NotificationService::current()->Notify(
84 chrome::NOTIFICATION_BROWSER_CLOSED,
85 content::Source<Browser>(browser),
86 content::NotificationService::NoDetails());
87
88 RemoveBrowserFrom(browser, &browser_list->browsers_);
89
90 FOR_EACH_OBSERVER(chrome::BrowserListObserver, observers_.Get(),
91 OnBrowserRemoved(browser));
92
93 g_browser_process->ReleaseModule();
94
95 // If we're exiting, send out the APP_TERMINATING notification to allow other
96 // modules to shut themselves down.
97 if (chrome::GetTotalBrowserCount() == 0 &&
98 (browser_shutdown::IsTryingToQuit() ||
99 g_browser_process->IsShuttingDown())) {
100 // Last browser has just closed, and this is a user-initiated quit or there
101 // is no module keeping the app alive, so send out our notification. No need
102 // to call ProfileManager::ShutdownSessionServices() as part of the
103 // shutdown, because Browser::WindowClosing() already makes sure that the
104 // SessionService is created and notified.
105 chrome::NotifyAppTerminating();
106 chrome::OnAppExiting();
107 }
108 }
109
110 // static
AddObserver(chrome::BrowserListObserver * observer)111 void BrowserList::AddObserver(chrome::BrowserListObserver* observer) {
112 observers_.Get().AddObserver(observer);
113 }
114
115 // static
RemoveObserver(chrome::BrowserListObserver * observer)116 void BrowserList::RemoveObserver(chrome::BrowserListObserver* observer) {
117 observers_.Get().RemoveObserver(observer);
118 }
119
120 // static
CloseAllBrowsersWithProfile(Profile * profile)121 void BrowserList::CloseAllBrowsersWithProfile(Profile* profile) {
122 BrowserVector browsers_to_close;
123 for (chrome::BrowserIterator it; !it.done(); it.Next()) {
124 if (it->profile()->GetOriginalProfile() == profile->GetOriginalProfile())
125 browsers_to_close.push_back(*it);
126 }
127
128 for (BrowserVector::const_iterator it = browsers_to_close.begin();
129 it != browsers_to_close.end(); ++it) {
130 (*it)->window()->Close();
131 }
132 }
133
134 // static
CloseAllBrowsersWithProfile(Profile * profile,const base::Callback<void (const base::FilePath &)> & on_close_success)135 void BrowserList::CloseAllBrowsersWithProfile(Profile* profile,
136 const base::Callback<void(const base::FilePath&)>& on_close_success) {
137 BrowserVector browsers_to_close;
138 for (chrome::BrowserIterator it; !it.done(); it.Next()) {
139 if (it->profile()->GetOriginalProfile() == profile->GetOriginalProfile())
140 browsers_to_close.push_back(*it);
141 }
142
143 TryToCloseBrowserList(browsers_to_close,
144 on_close_success,
145 profile->GetPath());
146 }
147
148 // static
TryToCloseBrowserList(const BrowserVector & browsers_to_close,const base::Callback<void (const base::FilePath &)> & on_close_success,const base::FilePath & profile_path)149 void BrowserList::TryToCloseBrowserList(const BrowserVector& browsers_to_close,
150 const base::Callback<void(const base::FilePath&)>& on_close_success,
151 const base::FilePath& profile_path) {
152 for (BrowserVector::const_iterator it = browsers_to_close.begin();
153 it != browsers_to_close.end(); ++it) {
154 if ((*it)->CallBeforeUnloadHandlers(
155 base::Bind(&BrowserList::PostBeforeUnloadHandlers,
156 browsers_to_close,
157 on_close_success,
158 profile_path))) {
159 return;
160 }
161 }
162
163 on_close_success.Run(profile_path);
164
165 for (Browser* b : browsers_to_close) {
166 // BeforeUnload handlers may close browser windows, so we need to explicitly
167 // check whether they still exist.
168 if (b->window())
169 b->window()->Close();
170 }
171 }
172
173 // static
PostBeforeUnloadHandlers(const BrowserVector & browsers_to_close,const base::Callback<void (const base::FilePath &)> & on_close_success,const base::FilePath & profile_path,bool tab_close_confirmed)174 void BrowserList::PostBeforeUnloadHandlers(
175 const BrowserVector& browsers_to_close,
176 const base::Callback<void(const base::FilePath&)>& on_close_success,
177 const base::FilePath& profile_path,
178 bool tab_close_confirmed) {
179 // We need this bool to avoid infinite recursion when resetting the
180 // BeforeUnload handlers, since doing that will trigger calls back to this
181 // method for each affected window.
182 static bool resetting_handlers = false;
183
184 if (tab_close_confirmed) {
185 TryToCloseBrowserList(browsers_to_close, on_close_success, profile_path);
186 } else if (!resetting_handlers) {
187 base::AutoReset<bool> resetting_handlers_scoper(&resetting_handlers, true);
188 for (BrowserVector::const_iterator it = browsers_to_close.begin();
189 it != browsers_to_close.end(); ++it) {
190 (*it)->ResetBeforeUnloadHandlers();
191 }
192 }
193 }
194
195 // static
SetLastActive(Browser * browser)196 void BrowserList::SetLastActive(Browser* browser) {
197 content::RecordAction(UserMetricsAction("ActiveBrowserChanged"));
198 BrowserList* browser_list = GetInstance(browser->host_desktop_type());
199
200 RemoveBrowserFrom(browser, &browser_list->last_active_browsers_);
201 browser_list->last_active_browsers_.push_back(browser);
202
203 FOR_EACH_OBSERVER(chrome::BrowserListObserver, observers_.Get(),
204 OnBrowserSetLastActive(browser));
205 }
206
207 // static
IsOffTheRecordSessionActive()208 bool BrowserList::IsOffTheRecordSessionActive() {
209 for (chrome::BrowserIterator it; !it.done(); it.Next()) {
210 if (it->profile()->IsOffTheRecord())
211 return true;
212 }
213 return false;
214 }
215
216 // static
IsOffTheRecordSessionActiveForProfile(Profile * profile)217 bool BrowserList::IsOffTheRecordSessionActiveForProfile(Profile* profile) {
218 if (profile->IsGuestSession())
219 return true;
220 for (chrome::BrowserIterator it; !it.done(); it.Next()) {
221 if (it->profile()->IsSameProfile(profile) &&
222 it->profile()->IsOffTheRecord()) {
223 return true;
224 }
225 }
226 return false;
227 }
228
229 ////////////////////////////////////////////////////////////////////////////////
230 // BrowserList, private:
231
BrowserList()232 BrowserList::BrowserList() {
233 }
234
~BrowserList()235 BrowserList::~BrowserList() {
236 }
237
238 // static
RemoveBrowserFrom(Browser * browser,BrowserVector * browser_list)239 void BrowserList::RemoveBrowserFrom(Browser* browser,
240 BrowserVector* browser_list) {
241 BrowserVector::iterator remove_browser =
242 std::find(browser_list->begin(), browser_list->end(), browser);
243 if (remove_browser != browser_list->end())
244 browser_list->erase(remove_browser);
245 }
246