browser_list.cc revision 29b820f8d84e3bc97d62552e54923c42407f2f29
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
25using base::UserMetricsAction;
26using content::WebContents;
27
28// static
29base::LazyInstance<ObserverList<chrome::BrowserListObserver> >::Leaky
30    BrowserList::observers_ = LAZY_INSTANCE_INITIALIZER;
31
32// static
33BrowserList* BrowserList::native_instance_ = NULL;
34BrowserList* BrowserList::ash_instance_ = NULL;
35
36////////////////////////////////////////////////////////////////////////////////
37// BrowserList, public:
38
39Browser* BrowserList::GetLastActive() const {
40  if (!last_active_browsers_.empty())
41    return *(last_active_browsers_.rbegin());
42  return NULL;
43}
44
45// static
46BrowserList* 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
60void 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
78void 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
111void BrowserList::AddObserver(chrome::BrowserListObserver* observer) {
112  observers_.Get().AddObserver(observer);
113}
114
115// static
116void BrowserList::RemoveObserver(chrome::BrowserListObserver* observer) {
117  observers_.Get().RemoveObserver(observer);
118}
119
120// static
121void 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
135void 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
149void 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
174void 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
196void 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
208bool 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
217bool 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
232BrowserList::BrowserList() {
233}
234
235BrowserList::~BrowserList() {
236}
237
238// static
239void 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