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 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 30 TabCloseableStateWatcher::TabStripWatcher::~TabStripWatcher() { 31 browser_->tabstrip_model()->RemoveObserver(this); 32 } 33 34 //////////////////////////////////////////////////////////////////////////////// 35 // TabCloseableStateWatcher::TabStripWatcher, 36 // TabStripModelObserver implementation: 37 38 void TabCloseableStateWatcher::TabStripWatcher::TabInsertedAt( 39 TabContentsWrapper* tab_contents, int index, bool foreground) { 40 main_watcher_->OnTabStripChanged(browser_, false); 41 } 42 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 52 void TabCloseableStateWatcher::TabStripWatcher::TabDetachedAt( 53 TabContentsWrapper* tab_contents, int index) { 54 main_watcher_->OnTabStripChanged(browser_, false); 55 } 56 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 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 77 TabCloseableStateWatcher::~TabCloseableStateWatcher() { 78 BrowserList::RemoveObserver(this); 79 if (!browser_shutdown::ShuttingDownWithoutClosingBrowsers()) 80 DCHECK(tabstrip_watchers_.empty()); 81 } 82 83 bool TabCloseableStateWatcher::CanCloseTab(const Browser* browser) const { 84 return browser->type() != Browser::TYPE_NORMAL ? true : 85 (can_close_tab_ || waiting_for_browser_); 86 } 87 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 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 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 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 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 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 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 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 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