1// Copyright 2013 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/lifetime/browser_close_manager.h"
6
7#include "chrome/browser/background/background_mode_manager.h"
8#include "chrome/browser/browser_process.h"
9#include "chrome/browser/browser_shutdown.h"
10#include "chrome/browser/download/download_service.h"
11#include "chrome/browser/download/download_service_factory.h"
12#include "chrome/browser/profiles/profile_manager.h"
13#include "chrome/browser/ui/browser.h"
14#include "chrome/browser/ui/browser_iterator.h"
15#include "chrome/browser/ui/browser_list.h"
16#include "chrome/browser/ui/browser_window.h"
17#include "chrome/browser/ui/chrome_pages.h"
18#include "chrome/browser/ui/scoped_tabbed_browser_displayer.h"
19#include "chrome/browser/ui/tabs/tab_strip_model.h"
20#include "content/public/browser/web_contents.h"
21
22BrowserCloseManager::BrowserCloseManager() : current_browser_(NULL) {}
23
24BrowserCloseManager::~BrowserCloseManager() {}
25
26void BrowserCloseManager::StartClosingBrowsers() {
27  // If the session is ending, skip straight to closing the browsers. There's no
28  // time to wait for beforeunload dialogs.
29  if (browser_shutdown::GetShutdownType() == browser_shutdown::END_SESSION) {
30    // Tell everyone that we are shutting down.
31    browser_shutdown::SetTryingToQuit(true);
32    CloseBrowsers();
33    return;
34  }
35  TryToCloseBrowsers();
36}
37
38void BrowserCloseManager::CancelBrowserClose() {
39  browser_shutdown::SetTryingToQuit(false);
40  for (chrome::BrowserIterator it; !it.done(); it.Next()) {
41    it->ResetBeforeUnloadHandlers();
42  }
43}
44
45void BrowserCloseManager::TryToCloseBrowsers() {
46  // If all browser windows can immediately be closed, fall out of this loop and
47  // close the browsers. If any browser window cannot be closed, temporarily
48  // stop closing. CallBeforeUnloadHandlers prompts the user and calls
49  // OnBrowserReportCloseable with the result. If the user confirms the close,
50  // this will trigger TryToCloseBrowsers to try again.
51  for (chrome::BrowserIterator it; !it.done(); it.Next()) {
52    if (it->CallBeforeUnloadHandlers(
53            base::Bind(&BrowserCloseManager::OnBrowserReportCloseable, this))) {
54      current_browser_ = *it;
55      return;
56    }
57  }
58  CheckForDownloadsInProgress();
59}
60
61void BrowserCloseManager::OnBrowserReportCloseable(bool proceed) {
62  if (!current_browser_)
63    return;
64
65  current_browser_ = NULL;
66
67  if (proceed)
68    TryToCloseBrowsers();
69  else
70    CancelBrowserClose();
71}
72
73void BrowserCloseManager::CheckForDownloadsInProgress() {
74  int download_count = DownloadService::NonMaliciousDownloadCountAllProfiles();
75  if (download_count == 0) {
76    CloseBrowsers();
77    return;
78  }
79  ConfirmCloseWithPendingDownloads(
80      download_count,
81      base::Bind(&BrowserCloseManager::OnReportDownloadsCancellable, this));
82}
83
84void BrowserCloseManager::ConfirmCloseWithPendingDownloads(
85    int download_count,
86    const base::Callback<void(bool)>& callback) {
87  Browser* browser =
88      BrowserList::GetInstance(chrome::GetActiveDesktop())->GetLastActive();
89  DCHECK(browser);
90  browser->window()->ConfirmBrowserCloseWithPendingDownloads(
91      download_count,
92      Browser::DOWNLOAD_CLOSE_BROWSER_SHUTDOWN,
93      true,
94      callback);
95}
96
97void BrowserCloseManager::OnReportDownloadsCancellable(bool proceed) {
98  if (proceed) {
99    CloseBrowsers();
100    return;
101  }
102
103  CancelBrowserClose();
104
105  // Open the downloads page for each profile with downloads in progress.
106  std::vector<Profile*> profiles(
107      g_browser_process->profile_manager()->GetLoadedProfiles());
108  for (std::vector<Profile*>::iterator it = profiles.begin();
109       it != profiles.end();
110       ++it) {
111    DownloadService* download_service =
112        DownloadServiceFactory::GetForBrowserContext(*it);
113    if (download_service->NonMaliciousDownloadCount() > 0) {
114      chrome::ScopedTabbedBrowserDisplayer displayer(
115          *it, chrome::GetActiveDesktop());
116      chrome::ShowDownloads(displayer.browser());
117    }
118  }
119}
120
121void BrowserCloseManager::CloseBrowsers() {
122#if defined(ENABLE_SESSION_SERVICE)
123  // Before we close the browsers shutdown all session services. That way an
124  // exit can restore all browsers open before exiting.
125  ProfileManager::ShutdownSessionServices();
126#endif
127  if (!browser_shutdown::IsTryingToQuit()) {
128    BackgroundModeManager* background_mode_manager =
129        g_browser_process->background_mode_manager();
130    if (background_mode_manager)
131      background_mode_manager->SuspendBackgroundMode();
132  }
133
134  bool session_ending =
135      browser_shutdown::GetShutdownType() == browser_shutdown::END_SESSION;
136  for (scoped_ptr<chrome::BrowserIterator> it_ptr(
137           new chrome::BrowserIterator());
138       !it_ptr->done();) {
139    Browser* browser = **it_ptr;
140    browser->window()->Close();
141    if (!session_ending) {
142      it_ptr->Next();
143    } else {
144      // This path is hit during logoff/power-down. In this case we won't get
145      // a final message and so we force the browser to be deleted.
146      // Close doesn't immediately destroy the browser
147      // (Browser::TabStripEmpty() uses invoke later) but when we're ending the
148      // session we need to make sure the browser is destroyed now. So, invoke
149      // DestroyBrowser to make sure the browser is deleted and cleanup can
150      // happen.
151      while (browser->tab_strip_model()->count())
152        delete browser->tab_strip_model()->GetWebContentsAt(0);
153      browser->window()->DestroyBrowser();
154      it_ptr.reset(new chrome::BrowserIterator());
155      if (!it_ptr->done() && browser == **it_ptr) {
156        // Destroying the browser should have removed it from the browser list.
157        // We should never get here.
158        NOTREACHED();
159        return;
160      }
161    }
162  }
163}
164