• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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