Home | History | Annotate | Download | only in chromeos
      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