1 // Copyright (c) 2011 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/chromeos/tab_closeable_state_watcher.h"
6
7 #include "base/command_line.h"
8 #include "chrome/browser/browser_shutdown.h"
9 #include "chrome/browser/profiles/profile.h"
10 #include "chrome/browser/tabs/tab_strip_model.h"
11 #include "chrome/browser/ui/tab_contents/tab_contents_wrapper.h"
12 #include "chrome/common/chrome_switches.h"
13 #include "chrome/common/url_constants.h"
14 #include "content/browser/tab_contents/tab_contents.h"
15 #include "content/browser/tab_contents/tab_contents_view.h"
16 #include "content/common/notification_service.h"
17
18 namespace chromeos {
19
20 ////////////////////////////////////////////////////////////////////////////////
21 // TabCloseableStateWatcher::TabStripWatcher, public:
22
TabStripWatcher(TabCloseableStateWatcher * main_watcher,const Browser * browser)23 TabCloseableStateWatcher::TabStripWatcher::TabStripWatcher(
24 TabCloseableStateWatcher* main_watcher, const Browser* browser)
25 : main_watcher_(main_watcher),
26 browser_(browser) {
27 browser_->tabstrip_model()->AddObserver(this);
28 }
29
~TabStripWatcher()30 TabCloseableStateWatcher::TabStripWatcher::~TabStripWatcher() {
31 browser_->tabstrip_model()->RemoveObserver(this);
32 }
33
34 ////////////////////////////////////////////////////////////////////////////////
35 // TabCloseableStateWatcher::TabStripWatcher,
36 // TabStripModelObserver implementation:
37
TabInsertedAt(TabContentsWrapper * tab_contents,int index,bool foreground)38 void TabCloseableStateWatcher::TabStripWatcher::TabInsertedAt(
39 TabContentsWrapper* tab_contents, int index, bool foreground) {
40 main_watcher_->OnTabStripChanged(browser_, false);
41 }
42
TabClosingAt(TabStripModel * tab_strip_model,TabContentsWrapper * tab_contents,int index)43 void TabCloseableStateWatcher::TabStripWatcher::TabClosingAt(
44 TabStripModel* tab_strip_model,
45 TabContentsWrapper* tab_contents,
46 int index) {
47 // Check if the last tab is closing.
48 if (tab_strip_model->count() == 1)
49 main_watcher_->OnTabStripChanged(browser_, true);
50 }
51
TabDetachedAt(TabContentsWrapper * tab_contents,int index)52 void TabCloseableStateWatcher::TabStripWatcher::TabDetachedAt(
53 TabContentsWrapper* tab_contents, int index) {
54 main_watcher_->OnTabStripChanged(browser_, false);
55 }
56
TabChangedAt(TabContentsWrapper * tab_contents,int index,TabChangeType change_type)57 void TabCloseableStateWatcher::TabStripWatcher::TabChangedAt(
58 TabContentsWrapper* tab_contents, int index, TabChangeType change_type) {
59 main_watcher_->OnTabStripChanged(browser_, false);
60 }
61
62 ////////////////////////////////////////////////////////////////////////////////
63 // TabCloseableStateWatcher, public:
64
TabCloseableStateWatcher()65 TabCloseableStateWatcher::TabCloseableStateWatcher()
66 : can_close_tab_(true),
67 signing_off_(false),
68 guest_session_(
69 CommandLine::ForCurrentProcess()->HasSwitch(
70 switches::kGuestSession)),
71 waiting_for_browser_(false) {
72 BrowserList::AddObserver(this);
73 notification_registrar_.Add(this, NotificationType::APP_EXITING,
74 NotificationService::AllSources());
75 }
76
~TabCloseableStateWatcher()77 TabCloseableStateWatcher::~TabCloseableStateWatcher() {
78 BrowserList::RemoveObserver(this);
79 if (!browser_shutdown::ShuttingDownWithoutClosingBrowsers())
80 DCHECK(tabstrip_watchers_.empty());
81 }
82
CanCloseTab(const Browser * browser) const83 bool TabCloseableStateWatcher::CanCloseTab(const Browser* browser) const {
84 return browser->type() != Browser::TYPE_NORMAL ? true :
85 (can_close_tab_ || waiting_for_browser_);
86 }
87
CanCloseBrowser(Browser * browser)88 bool TabCloseableStateWatcher::CanCloseBrowser(Browser* browser) {
89 BrowserActionType action_type;
90 bool can_close = CanCloseBrowserImpl(browser, &action_type);
91 if (action_type == OPEN_WINDOW) {
92 browser->NewWindow();
93 } else if (action_type == OPEN_NTP) {
94 // NTP will be opened before closing last tab (via TabStripModelObserver::
95 // TabClosingAt), close all tabs now.
96 browser->CloseAllTabs();
97 }
98 return can_close;
99 }
100
OnWindowCloseCanceled(Browser * browser)101 void TabCloseableStateWatcher::OnWindowCloseCanceled(Browser* browser) {
102 // This could be a call to cancel APP_EXITING if user doesn't proceed with
103 // unloading handler.
104 if (signing_off_) {
105 signing_off_ = false;
106 CheckAndUpdateState(browser);
107 }
108 }
109
110 ////////////////////////////////////////////////////////////////////////////////
111 // TabCloseableStateWatcher, BrowserList::Observer implementation:
112
OnBrowserAdded(const Browser * browser)113 void TabCloseableStateWatcher::OnBrowserAdded(const Browser* browser) {
114 waiting_for_browser_ = false;
115
116 // Only normal browsers may affect closeable state.
117 if (browser->type() != Browser::TYPE_NORMAL)
118 return;
119
120 // Create TabStripWatcher to observe tabstrip of new browser.
121 tabstrip_watchers_.push_back(new TabStripWatcher(this, browser));
122
123 // When a normal browser is just added, there's no tabs yet, so we wait till
124 // TabInsertedAt notification to check for change in state.
125 }
126
OnBrowserRemoved(const Browser * browser)127 void TabCloseableStateWatcher::OnBrowserRemoved(const Browser* browser) {
128 // Only normal browsers may affect closeable state.
129 if (browser->type() != Browser::TYPE_NORMAL)
130 return;
131
132 // Remove TabStripWatcher for browser that is being removed.
133 for (std::vector<TabStripWatcher*>::iterator it = tabstrip_watchers_.begin();
134 it != tabstrip_watchers_.end(); ++it) {
135 if ((*it)->browser() == browser) {
136 delete (*it);
137 tabstrip_watchers_.erase(it);
138 break;
139 }
140 }
141
142 CheckAndUpdateState(NULL);
143 }
144
145 ////////////////////////////////////////////////////////////////////////////////
146 // TabCloseableStateWatcher, NotificationObserver implementation:
147
Observe(NotificationType type,const NotificationSource & source,const NotificationDetails & details)148 void TabCloseableStateWatcher::Observe(NotificationType type,
149 const NotificationSource& source, const NotificationDetails& details) {
150 if (type.value != NotificationType::APP_EXITING)
151 NOTREACHED();
152 if (!signing_off_) {
153 signing_off_ = true;
154 SetCloseableState(true);
155 }
156 }
157
158 ////////////////////////////////////////////////////////////////////////////////
159 // TabCloseableStateWatcher, private
160
OnTabStripChanged(const Browser * browser,bool closing_last_tab)161 void TabCloseableStateWatcher::OnTabStripChanged(const Browser* browser,
162 bool closing_last_tab) {
163 if (waiting_for_browser_)
164 return;
165
166 if (!closing_last_tab) {
167 CheckAndUpdateState(browser);
168 return;
169 }
170
171 // Before closing last tab, open new window or NTP if necessary.
172 BrowserActionType action_type;
173 CanCloseBrowserImpl(browser, &action_type);
174 if (action_type != NONE) {
175 Browser* mutable_browser = const_cast<Browser*>(browser);
176 if (action_type == OPEN_WINDOW)
177 mutable_browser->NewWindow();
178 else if (action_type == OPEN_NTP)
179 mutable_browser->NewTab();
180 }
181 }
182
CheckAndUpdateState(const Browser * browser_to_check)183 void TabCloseableStateWatcher::CheckAndUpdateState(
184 const Browser* browser_to_check) {
185 if (waiting_for_browser_ || signing_off_ || tabstrip_watchers_.empty() ||
186 (browser_to_check && browser_to_check->type() != Browser::TYPE_NORMAL))
187 return;
188
189 bool new_can_close;
190
191 if (tabstrip_watchers_.size() > 1) {
192 new_can_close = true;
193 } else { // There's only 1 normal browser.
194 if (!browser_to_check)
195 browser_to_check = tabstrip_watchers_[0]->browser();
196 if (browser_to_check->profile()->IsOffTheRecord() && !guest_session_) {
197 new_can_close = true;
198 } else {
199 TabStripModel* tabstrip_model = browser_to_check->tabstrip_model();
200 if (tabstrip_model->count() == 1) {
201 new_can_close =
202 tabstrip_model->GetTabContentsAt(0)->tab_contents()->GetURL() !=
203 GURL(chrome::kChromeUINewTabURL); // Tab is not NewTabPage.
204 } else {
205 new_can_close = true;
206 }
207 }
208 }
209
210 SetCloseableState(new_can_close);
211 }
212
SetCloseableState(bool closeable)213 void TabCloseableStateWatcher::SetCloseableState(bool closeable) {
214 if (can_close_tab_ == closeable) // No change in state.
215 return;
216
217 can_close_tab_ = closeable;
218
219 // Notify of change in tab closeable state.
220 NotificationService::current()->Notify(
221 NotificationType::TAB_CLOSEABLE_STATE_CHANGED,
222 NotificationService::AllSources(),
223 Details<bool>(&can_close_tab_));
224 }
225
CanCloseBrowserImpl(const Browser * browser,BrowserActionType * action_type)226 bool TabCloseableStateWatcher::CanCloseBrowserImpl(
227 const Browser* browser,
228 BrowserActionType* action_type) {
229 *action_type = NONE;
230
231 // If we're waiting for a new browser allow the close.
232 if (waiting_for_browser_)
233 return true;
234
235 // Browser is always closeable when signing off.
236 if (signing_off_)
237 return true;
238
239 // Non-normal browsers are always closeable.
240 if (browser->type() != Browser::TYPE_NORMAL)
241 return true;
242
243 // If this is not the last normal browser, it's always closeable.
244 if (tabstrip_watchers_.size() > 1)
245 return true;
246
247 // If last normal browser is incognito, open a non-incognito window, and allow
248 // closing of incognito one (if not guest). When this happens we need to wait
249 // for the new browser before doing any other actions as the new browser may
250 // be created by way of session restore, which is async.
251 if (browser->profile()->IsOffTheRecord() && !guest_session_) {
252 *action_type = OPEN_WINDOW;
253 waiting_for_browser_ = true;
254 return true;
255 }
256
257 // If tab is not closeable, browser is not closeable.
258 if (!can_close_tab_)
259 return false;
260
261 // Otherwise, close existing tabs, and deny closing of browser.
262 // TabClosingAt will open NTP when the last tab is being closed.
263 *action_type = OPEN_NTP;
264 return false;
265 }
266
267 } // namespace chromeos
268